This commit is contained in:
rebelonion 2024-04-04 04:49:08 -05:00
commit 7688ffa39f
11 changed files with 644 additions and 656 deletions

View file

@ -92,6 +92,7 @@ import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.BuildConfig.APPLICATION_ID
import ani.dantotsu.connections.anilist.Genre
import ani.dantotsu.connections.anilist.api.FuzzyDate
import ani.dantotsu.connections.bakaupdates.MangaUpdates
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.databinding.ItemCountDownBinding
import ani.dantotsu.media.Media
@ -102,6 +103,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.settings.saving.internal.PreferenceKeystore
import ani.dantotsu.settings.saving.internal.PreferenceKeystore.Companion.generateSalt
import ani.dantotsu.util.CountUpTimer
import ani.dantotsu.util.Logger
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder
@ -134,9 +136,12 @@ import io.noties.markwon.html.TagHandlerNoOp
import io.noties.markwon.image.AsyncDrawable
import io.noties.markwon.image.glide.GlideImagesPlugin
import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import nl.joery.animatedbottombar.AnimatedBottomBar
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -985,6 +990,54 @@ fun countDown(media: Media, view: ViewGroup) {
}
}
fun sinceWhen(media: Media, view: ViewGroup) {
CoroutineScope(Dispatchers.IO).launch {
MangaUpdates().search(media.name ?: media.nameRomaji, media.startDate)?.let {
val latestChapter = it.metadata.series.latestChapter ?: it.record.chapter?.let { chapter ->
if (chapter.contains("-"))
chapter.split("-")[1].trim()
else
chapter
}?.toInt()
val timeSince = (System.currentTimeMillis() -
(it.metadata.series.lastUpdated!!.timestamp * 1000)) / 1000
withContext(Dispatchers.Main) {
val v =
ItemCountDownBinding.inflate(LayoutInflater.from(view.context), view, false)
view.addView(v.root, 0)
v.mediaCountdownText.text =
currActivity()?.getString(R.string.chapter_release_timeout, latestChapter)
object : CountUpTimer(86400000) {
override fun onTick(second: Int) {
val a = second + timeSince
v.mediaCountdown.text = currActivity()?.getString(
R.string.time_format,
a / 86400,
a % 86400 / 3600,
a % 86400 % 3600 / 60,
a % 86400 % 3600 % 60
)
}
override fun onFinish() {
// The legend will never die.
}
}.start()
}
}
}
}
fun displayTimer(media: Media, view: ViewGroup) {
when {
media.anime != null -> countDown(media, view)
media.format == "MANGA" || media.format == "ONE_SHOT" -> sinceWhen(media, view)
else -> { } // No timer yet
}
}
fun MutableMap<String, Genre>.checkId(id: Int): Boolean {
this.forEach {
if (it.value.id == id) {

View file

@ -0,0 +1,98 @@
package ani.dantotsu.connections.bakaupdates
import ani.dantotsu.client
import ani.dantotsu.connections.anilist.api.FuzzyDate
import ani.dantotsu.tryWithSuspend
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import okio.ByteString.Companion.encode
import org.json.JSONException
import org.json.JSONObject
import java.nio.charset.Charset
class MangaUpdates {
private val Int?.dateFormat get() = String.format("%02d", this)
private val apiUrl = "https://api.mangaupdates.com/v1/releases/search"
suspend fun search(title: String, startDate: FuzzyDate?) : MangaUpdatesResponse.Results? {
return tryWithSuspend {
val query = JSONObject().apply {
try {
put("search", title.encode(Charset.forName("UTF-8")))
startDate?.let {
put(
"start_date",
"${it.year}-${it.month.dateFormat}-${it.day.dateFormat}"
)
}
put("include_metadata", true)
} catch (e: JSONException) {
e.printStackTrace()
}
}
val res = client.post(apiUrl, json = query).parsed<MangaUpdatesResponse>()
res.results?.forEach{ println("MangaUpdates: $it") }
res.results?.first { it.metadata.series.lastUpdated?.timestamp != null }
}
}
@Serializable
data class MangaUpdatesResponse(
@SerialName("total_hits")
val totalHits: Int?,
@SerialName("page")
val page: Int?,
@SerialName("per_page")
val perPage: Int?,
val results: List<Results>? = null
) {
@Serializable
data class Results(
val record: Record,
val metadata: MetaData
) {
@Serializable
data class Record(
@SerialName("id")
val id: Int,
@SerialName("title")
val title: String,
@SerialName("volume")
val volume: String?,
@SerialName("chapter")
val chapter: String?,
@SerialName("release_date")
val releaseDate: String
)
@Serializable
data class MetaData(
val series: Series
) {
@Serializable
data class Series(
@SerialName("series_id")
val seriesId: Long?,
@SerialName("title")
val title: String?,
@SerialName("latest_chapter")
val latestChapter: Int?,
@SerialName("last_updated")
val lastUpdated: LastUpdated?
) {
@Serializable
data class LastUpdated(
@SerialName("timestamp")
val timestamp: Long,
@SerialName("as_rfc3339")
val asRfc3339: String,
@SerialName("as_string")
val asString: String
)
}
}
}
}
}

View file

@ -27,7 +27,6 @@ import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.GenresViewModel
import ani.dantotsu.copyToClipboard
import ani.dantotsu.countDown
import ani.dantotsu.currActivity
import ani.dantotsu.databinding.ActivityGenreBinding
import ani.dantotsu.databinding.FragmentMediaInfoBinding
@ -38,6 +37,7 @@ import ani.dantotsu.databinding.ItemTitleRecyclerBinding
import ani.dantotsu.databinding.ItemTitleSearchBinding
import ani.dantotsu.databinding.ItemTitleTextBinding
import ani.dantotsu.databinding.ItemTitleTrailerBinding
import ani.dantotsu.displayTimer
import ani.dantotsu.loadImage
import ani.dantotsu.navBarHeight
import ani.dantotsu.px
@ -225,8 +225,7 @@ class MediaInfoFragment : Fragment() {
.setDuration(400).start()
}
}
countDown(media, binding.mediaInfoContainer)
displayTimer(media, binding.mediaInfoContainer)
val parent = _binding?.mediaInfoContainer!!
val screenWidth = resources.displayMetrics.run { widthPixels / density }

View file

@ -17,11 +17,11 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.FileUrl
import ani.dantotsu.R
import ani.dantotsu.countDown
import ani.dantotsu.currActivity
import ani.dantotsu.databinding.DialogLayoutBinding
import ani.dantotsu.databinding.ItemAnimeWatchBinding
import ani.dantotsu.databinding.ItemChipBinding
import ani.dantotsu.displayTimer
import ani.dantotsu.isOnline
import ani.dantotsu.loadImage
import ani.dantotsu.media.Media
@ -500,8 +500,7 @@ class AnimeWatchAdapter(
inner class ViewHolder(val binding: ItemAnimeWatchBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
//Timer
countDown(media, binding.animeSourceContainer)
displayTimer(media, binding.animeSourceContainer)
}
}
}

View file

@ -6,7 +6,6 @@ import android.content.res.Configuration
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.PopupMenu
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
@ -23,6 +22,7 @@ import ani.dantotsu.blurImage
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Query
import ani.dantotsu.databinding.ActivityProfileBinding
import ani.dantotsu.databinding.ItemProfileAppBarBinding
import ani.dantotsu.initActivity
import ani.dantotsu.loadImage
import ani.dantotsu.media.user.ListActivity
@ -46,6 +46,7 @@ import kotlin.math.abs
class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListener {
lateinit var binding: ActivityProfileBinding
private lateinit var bindingProfileAppBar: ItemProfileAppBarBinding
private var selected: Int = 0
lateinit var navBar: AnimatedBottomBar
@ -109,147 +110,165 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
binding.profileViewPager.setCurrentItem(selected, true)
}
})
val userLevel = intent.getStringExtra("userLVL") ?: ""
binding.followButton.isGone = user.id == Anilist.userid || Anilist.userid == null
binding.followButton.text = getString(
when {
user.isFollowing -> R.string.unfollow
user.isFollower -> R.string.follows_you
else -> R.string.follow
}
)
if (user.isFollowing && user.isFollower) binding.followButton.text = getString(R.string.mutual)
binding.followButton.setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
val res = Anilist.query.toggleFollow(user.id)
if (res?.data?.toggleFollow != null) {
withContext(Dispatchers.Main) {
snackString(R.string.success)
user.isFollowing = res.data.toggleFollow.isFollowing
binding.followButton.text = getString(
when {
user.isFollowing -> R.string.unfollow
user.isFollower -> R.string.follows_you
else -> R.string.follow
}
)
if (user.isFollowing && user.isFollower)
binding.followButton.text = getString(R.string.mutual)
bindingProfileAppBar = ItemProfileAppBarBinding.bind(binding.root).apply {
val userLevel = intent.getStringExtra("userLVL") ?: ""
followButton.isGone =
user.id == Anilist.userid || Anilist.userid == null
followButton.text = getString(
when {
user.isFollowing -> R.string.unfollow
user.isFollower -> R.string.follows_you
else -> R.string.follow
}
)
if (user.isFollowing && user.isFollower) followButton.text =
getString(R.string.mutual)
followButton.setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
val res = Anilist.query.toggleFollow(user.id)
if (res?.data?.toggleFollow != null) {
withContext(Dispatchers.Main) {
snackString(R.string.success)
user.isFollowing = res.data.toggleFollow.isFollowing
followButton.text = getString(
when {
user.isFollowing -> R.string.unfollow
user.isFollower -> R.string.follows_you
else -> R.string.follow
}
)
if (user.isFollowing && user.isFollower)
followButton.text = getString(R.string.mutual)
}
}
}
}
}
binding.profileProgressBar.visibility = View.GONE
binding.profileAppBar.visibility = View.VISIBLE
binding.profileMenuButton.setOnClickListener {
val popup = PopupMenu(this@ProfileActivity, binding.profileMenuButton)
popup.menuInflater.inflate(R.menu.menu_profile, popup.menu)
popup.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_view_following -> {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", "Following")
.putExtra("userId", user.id),
null
)
true
}
binding.profileProgressBar.visibility = View.GONE
profileAppBar.visibility = View.VISIBLE
profileMenuButton.setOnClickListener {
val popup = PopupMenu(this@ProfileActivity, profileMenuButton)
popup.menuInflater.inflate(R.menu.menu_profile, popup.menu)
popup.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_view_following -> {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", "Following")
.putExtra("userId", user.id),
null
)
true
}
R.id.action_view_followers -> {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", "Followers")
.putExtra("userId", user.id),
null
)
true
}
R.id.action_view_followers -> {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", "Followers")
.putExtra("userId", user.id),
null
)
true
}
R.id.action_view_on_anilist -> {
openLinkInBrowser("https://anilist.co/user/${user.name}")
true
}
R.id.action_view_on_anilist -> {
openLinkInBrowser("https://anilist.co/user/${user.name}")
true
}
else -> false
else -> false
}
}
popup.show()
}
popup.show()
}
binding.profileUserAvatar.loadImage(user.avatar?.medium)
binding.profileUserAvatar.setOnLongClickListener {
ImageViewDialog.newInstance(
this@ProfileActivity,
"${user.name}'s [Avatar]",
user.avatar?.medium
profileUserAvatar.loadImage(user.avatar?.medium)
profileUserAvatar.setOnLongClickListener {
ImageViewDialog.newInstance(
this@ProfileActivity,
"${user.name}'s [Avatar]",
user.avatar?.medium
)
}
val userLevelText = "${user.name} $userLevel"
profileUserName.text = userLevelText
val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
blurImage(
if (bannerAnimations) profileBannerImage else profileBannerImageNoKen,
user.bannerImage ?: user.avatar?.medium
)
}
profileBannerImage.updateLayoutParams { height += statusBarHeight }
profileBannerImageNoKen.updateLayoutParams { height += statusBarHeight }
profileBannerGradient.updateLayoutParams { height += statusBarHeight }
profileCloseButton.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
profileMenuButton.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
profileButtonContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
profileBannerImage.setOnLongClickListener {
ImageViewDialog.newInstance(
this@ProfileActivity,
user.name + " [Banner]",
user.bannerImage
)
}
val userLevelText = "${user.name} $userLevel"
binding.profileUserName.text = userLevelText
val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
blurImage(if (bannerAnimations) binding.profileBannerImage else binding.profileBannerImageNoKen as ImageView, user.bannerImage ?: user.avatar?.medium)
binding.profileBannerImage.updateLayoutParams { height += statusBarHeight }
binding.profileBannerImageNoKen?.updateLayoutParams { height += statusBarHeight }
binding.profileBannerGradient.updateLayoutParams { height += statusBarHeight }
binding.profileMenuButton.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
binding.profileButtonContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
binding.profileBannerImage.setOnLongClickListener {
ImageViewDialog.newInstance(
this@ProfileActivity,
user.name + " [Banner]",
user.bannerImage
)
}
mMaxScrollSize = binding.profileAppBar.totalScrollRange
binding.profileAppBar.addOnOffsetChangedListener(this@ProfileActivity)
mMaxScrollSize = profileAppBar.totalScrollRange
profileAppBar.addOnOffsetChangedListener(this@ProfileActivity)
binding.profileFollowerCount.text = followers.toString()
binding.profileFollowerCountContainer.setOnClickListener {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", getString(R.string.followers))
.putExtra("userId", user.id),
null
)
}
profileFollowerCount.text = followers.toString()
profileFollowerCountContainer.setOnClickListener {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", getString(R.string.followers))
.putExtra("userId", user.id),
null
)
}
binding.profileFollowingCount.text = following.toString()
binding.profileFollowingCountContainer.setOnClickListener {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", "Following")
.putExtra("userId", user.id),
null
)
}
profileFollowingCount.text = following.toString()
profileFollowingCountContainer.setOnClickListener {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, FollowActivity::class.java)
.putExtra("title", "Following")
.putExtra("userId", user.id),
null
)
}
binding.profileAnimeCount.text = user.statistics.anime.count.toString()
binding.profileAnimeCountContainer.setOnClickListener {
ContextCompat.startActivity(
this@ProfileActivity, Intent(this@ProfileActivity, ListActivity::class.java)
.putExtra("anime", true)
.putExtra("userId", user.id)
.putExtra("username", user.name), null
)
}
profileAnimeCount.text = user.statistics.anime.count.toString()
profileAnimeCountContainer.setOnClickListener {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, ListActivity::class.java)
.putExtra("anime", true)
.putExtra("userId", user.id)
.putExtra("username", user.name),
null
)
}
binding.profileMangaCount.text = user.statistics.manga.count.toString()
binding.profileMangaCountContainer.setOnClickListener {
ContextCompat.startActivity(
this@ProfileActivity, Intent(this@ProfileActivity, ListActivity::class.java)
.putExtra("anime", false)
.putExtra("userId", user.id)
.putExtra("username", user.name), null
)
profileMangaCount.text = user.statistics.manga.count.toString()
profileMangaCountContainer.setOnClickListener {
ContextCompat.startActivity(
this@ProfileActivity,
Intent(this@ProfileActivity, ListActivity::class.java)
.putExtra("anime", false)
.putExtra("userId", user.id)
.putExtra("username", user.name),
null
)
}
profileCloseButton.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
}
}
@ -265,29 +284,31 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
if (mMaxScrollSize == 0) mMaxScrollSize = appBar.totalScrollRange
val percentage = abs(i) * 100 / mMaxScrollSize
binding.profileUserAvatarContainer.visibility =
if (binding.profileUserAvatarContainer.scaleX == 0f) View.GONE else View.VISIBLE
val duration = (200 * (PrefManager.getVal(PrefName.AnimationSpeed) as Float)).toLong()
if (percentage >= percent && !isCollapsed) {
isCollapsed = true
ObjectAnimator.ofFloat(binding.profileUserDataContainer, "translationX", screenWidth)
.setDuration(duration).start()
ObjectAnimator.ofFloat(binding.profileUserAvatarContainer, "translationX", screenWidth)
.setDuration(duration).start()
ObjectAnimator.ofFloat(binding.profileButtonContainer, "translationX", screenWidth)
.setDuration(duration).start()
binding.profileBannerImage.pause()
}
if (percentage <= percent && isCollapsed) {
isCollapsed = false
ObjectAnimator.ofFloat(binding.profileUserDataContainer, "translationX", 0f)
.setDuration(duration).start()
ObjectAnimator.ofFloat(binding.profileUserAvatarContainer, "translationX", 0f)
.setDuration(duration).start()
ObjectAnimator.ofFloat(binding.profileButtonContainer, "translationX", 0f)
.setDuration(duration).start()
with (bindingProfileAppBar) {
profileUserAvatarContainer.visibility =
if (profileUserAvatarContainer.scaleX == 0f) View.GONE else View.VISIBLE
val duration = (200 * (PrefManager.getVal(PrefName.AnimationSpeed) as Float)).toLong()
if (percentage >= percent && !isCollapsed) {
isCollapsed = true
ObjectAnimator.ofFloat(profileUserDataContainer, "translationX", screenWidth)
.setDuration(duration).start()
ObjectAnimator.ofFloat(profileUserAvatarContainer, "translationX", screenWidth)
.setDuration(duration).start()
ObjectAnimator.ofFloat(profileButtonContainer, "translationX", screenWidth)
.setDuration(duration).start()
profileBannerImage.pause()
}
if (percentage <= percent && isCollapsed) {
isCollapsed = false
ObjectAnimator.ofFloat(profileUserDataContainer, "translationX", 0f)
.setDuration(duration).start()
ObjectAnimator.ofFloat(profileUserAvatarContainer, "translationX", 0f)
.setDuration(duration).start()
ObjectAnimator.ofFloat(profileButtonContainer, "translationX", 0f)
.setDuration(duration).start()
if (PrefManager.getVal(PrefName.BannerAnimations)) binding.profileBannerImage.resume()
if (PrefManager.getVal(PrefName.BannerAnimations)) profileBannerImage.resume()
}
}
}

View file

@ -0,0 +1,22 @@
package ani.dantotsu.util
import android.os.CountDownTimer
// https://stackoverflow.com/a/40422151/461982
abstract class CountUpTimer protected constructor(
private val duration: Long
) : CountDownTimer(duration, INTERVAL_MS) {
abstract fun onTick(second: Int)
override fun onTick(msUntilFinished: Long) {
val second = ((duration - msUntilFinished) / 1000).toInt()
onTick(second)
}
override fun onFinish() {
onTick(duration / 1000)
}
companion object {
private const val INTERVAL_MS: Long = 1000
}
}

View file

@ -0,0 +1,17 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:tint="#000000"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp">
<group
android:translateX="24"
android:rotation="90">
<path
android:fillColor="@android:color/white"
android:pathData="M12,4c4.41,0 8,3.59 8,8s-3.59,8 -8,8s-8,-3.59 -8,-8S7.59,4 12,4M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2L12,2zM13,12l0,-4h-2l0,4H8l4,4l4,-4H13z"/>
</group>
</vector>

View file

@ -24,253 +24,7 @@
android:layout_height="wrap_content" />
</LinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/profileAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:translationZ="5dp"
android:visibility="gone"
tools:visibility="visible">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="false"
app:contentScrim="?android:colorBackground"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.flaviofaria.kenburnsview.KenBurnsView
android:id="@+id/profileBannerImage"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
tools:ignore="ContentDescription"
tools:src="@tools:sample/backgrounds/scenic" />
<ImageView
android:id="@+id/profileBannerGradient"
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="@drawable/linear_gradient_bg"
tools:ignore="ContentDescription" />
<LinearLayout
android:id="@+id/profileUserDataContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="8dp"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:id="@+id/profileUserAvatarContainer"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_gravity="center"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp"
app:strokeColor="@color/transparent">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/profileUserAvatar"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_round_add_circle_24"
tools:ignore="ContentDescription,ImageContrastCheck"
tools:tint="@color/transparent" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/profileUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/username"
android:textColor="?attr/colorPrimary"
android:textSize="18sp" />
<Button
android:id="@+id/followButton"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="?attr/colorSecondaryContainer"
android:enabled="true"
android:fontFamily="@font/poppins_bold"
android:text="@string/follow"
android:textColor="@color/bg_opp"
android:textSize="14sp"
app:cornerRadius="8dp"
app:strokeColor="?attr/colorSecondaryContainer"
tools:ignore="SpeakableTextPresentCheck" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="20dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/profileMenuButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:contentDescription="@string/menu"
android:src="@drawable/ic_round_dots_vertical_24"
app:tint="@color/bg_opp" />
</LinearLayout>
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/profileButtonContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_marginTop="200dp"
android:layout_marginBottom="2dp"
android:backgroundTint="?attr/colorSurfaceVariant"
android:baselineAligned="false"
app:cardCornerRadius="24dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/profileFollowerCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileFollowerCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/followers" />
</LinearLayout>
<View
android:layout_width="3dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/profileFollowingCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileFollowingCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/following" />
</LinearLayout>
<View
android:layout_width="3dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/profileAnimeCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileAnimeCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="3" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/anime" />
</LinearLayout>
<View
android:layout_width="3dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/profileMangaCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileMangaCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="4" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/manga" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/item_profile_app_bar" />
<LinearLayout
android:layout_width="match_parent"

View file

@ -19,261 +19,7 @@
android:layout_height="wrap_content" />
</LinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/profileAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:translationZ="5dp"
android:visibility="gone"
tools:visibility="visible">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="false"
app:contentScrim="?android:colorBackground"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.flaviofaria.kenburnsview.KenBurnsView
android:id="@+id/profileBannerImage"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
tools:ignore="ContentDescription"
tools:src="@tools:sample/backgrounds/scenic" />
<ImageView
android:id="@+id/profileBannerImageNoKen"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
tools:ignore="ContentDescription"
tools:src="@tools:sample/backgrounds/scenic" />
<ImageView
android:id="@+id/profileBannerGradient"
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="@drawable/linear_gradient_bg"
tools:ignore="ContentDescription" />
<LinearLayout
android:id="@+id/profileUserDataContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="8dp"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:id="@+id/profileUserAvatarContainer"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_gravity="center"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp"
app:strokeColor="@color/transparent">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/profileUserAvatar"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_round_add_circle_24"
tools:ignore="ContentDescription,ImageContrastCheck"
tools:tint="@color/transparent" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/profileUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/username"
android:textColor="?attr/colorPrimary"
android:textSize="18sp" />
<Button
android:id="@+id/followButton"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="?attr/colorSecondaryContainer"
android:enabled="true"
android:fontFamily="@font/poppins_bold"
android:text="@string/follow"
android:textColor="@color/bg_opp"
android:textSize="14sp"
app:cornerRadius="8dp"
app:strokeColor="?attr/colorSecondaryContainer"
tools:ignore="SpeakableTextPresentCheck" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="20dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/profileMenuButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:contentDescription="@string/menu"
android:src="@drawable/ic_round_dots_vertical_24"
app:tint="@color/bg_opp" />
</LinearLayout>
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/profileButtonContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="12dp"
android:layout_marginTop="200dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="2dp"
android:backgroundTint="?attr/colorSurfaceVariant"
android:baselineAligned="false"
app:cardCornerRadius="24dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/profileFollowerCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileFollowerCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/followers" />
</LinearLayout>
<View
android:layout_width="3dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/profileFollowingCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileFollowingCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/following" />
</LinearLayout>
<View
android:layout_width="3dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/profileAnimeCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileAnimeCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="3" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/anime" />
</LinearLayout>
<View
android:layout_width="3dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/profileMangaCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileMangaCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="4" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/manga" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/item_profile_app_bar" />
<LinearLayout
android:layout_width="match_parent"

View file

@ -0,0 +1,278 @@
<?xml version="1.0" encoding="utf-8"?>
<merge 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">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/profileAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:translationZ="5dp"
android:visibility="gone"
tools:visibility="visible">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="false"
app:contentScrim="?android:colorBackground"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.flaviofaria.kenburnsview.KenBurnsView
android:id="@+id/profileBannerImage"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
tools:ignore="ContentDescription"
tools:src="@tools:sample/backgrounds/scenic" />
<ImageView
android:id="@+id/profileBannerImageNoKen"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
tools:ignore="ContentDescription"
tools:src="@tools:sample/backgrounds/scenic" />
<ImageView
android:id="@+id/profileBannerGradient"
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="@drawable/linear_gradient_bg"
tools:ignore="ContentDescription" />
<LinearLayout
android:id="@+id/profileUserDataContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="8dp"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:id="@+id/profileUserAvatarContainer"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_gravity="center"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp"
app:strokeColor="@color/transparent">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/profileUserAvatar"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_round_add_circle_24"
tools:ignore="ContentDescription,ImageContrastCheck"
tools:tint="@color/transparent" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/profileUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/username"
android:textColor="?attr/colorPrimary"
android:textSize="18sp" />
<Button
android:id="@+id/followButton"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="?attr/colorSecondaryContainer"
android:enabled="true"
android:fontFamily="@font/poppins_bold"
android:text="@string/follow"
android:textColor="@color/bg_opp"
android:textSize="14sp"
app:cornerRadius="8dp"
app:strokeColor="?attr/colorSecondaryContainer"
tools:ignore="SpeakableTextPresentCheck" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="20dp"
android:layout_marginStart="24dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/profileCloseButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:contentDescription="@string/menu"
android:src="@drawable/ic_circle_arrow_left_24"
app:tint="@color/bg_opp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="20dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/profileMenuButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:contentDescription="@string/menu"
android:src="@drawable/ic_round_dots_vertical_24"
app:tint="@color/bg_opp" />
</LinearLayout>
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/profileButtonContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="12dp"
android:layout_marginTop="200dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="2dp"
android:backgroundTint="?attr/colorSurfaceVariant"
android:baselineAligned="false"
app:cardCornerRadius="24dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/profileFollowerCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileFollowerCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/followers" />
</LinearLayout>
<View
android:layout_width="3dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/profileFollowingCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileFollowingCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/following" />
</LinearLayout>
<View
android:layout_width="3dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/profileAnimeCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileAnimeCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="3" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/anime" />
</LinearLayout>
<View
android:layout_width="3dp"
android:layout_height="match_parent"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/profileMangaCountContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/profileMangaCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:textColor="?attr/colorPrimary"
tools:text="4" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/manga" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
</merge>

View file

@ -503,6 +503,7 @@
<string name="refresh_token_load_failed">Refresh Token : Failed to load Saved Token</string>
<string name="refreshing_token_failed">Refreshing Token Failed</string>
<string name="episode_release_countdown">Episode %1$d will be released in</string>
<string name="chapter_release_timeout">Chapter %1$d has been available for</string>
<string name="time_format">%1$d days %2$d hrs %3$d mins %4$d secs</string>
<string-array name="sort_by">
<item>Score</item>