feat: activity view

This commit is contained in:
aayush262 2024-04-26 03:27:04 +05:30
parent 55ad8dccad
commit 856deb7755
16 changed files with 945 additions and 6 deletions

View file

@ -139,6 +139,9 @@
<activity <activity
android:name=".settings.SettingsAboutActivity" android:name=".settings.SettingsAboutActivity"
android:parentActivityName=".MainActivity" /> android:parentActivityName=".MainActivity" />
<activity
android:name=".home.StatusActivity"
android:parentActivityName=".MainActivity" />
<activity <activity
android:name=".settings.SettingsAccountActivity" android:name=".settings.SettingsAccountActivity"
android:parentActivityName=".MainActivity" /> android:parentActivityName=".MainActivity" />

View file

@ -1637,7 +1637,13 @@ Page(page:$page,perPage:50) {
force = true force = true
) )
} }
suspend fun getStatus(
): FeedResponse? {
return executeQuery<FeedResponse>(
"""{Page(page:1,perPage:50){activities(isFollowing: true, type:MEDIA_LIST,sort:ID_DESC){__typename ... on TextActivity{id userId type replyCount text(asHtml:true)siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on ListActivity{id userId type replyCount status progress siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}media{id title{english romaji native userPreferred}bannerImage coverImage{extraLarge medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on MessageActivity{id recipientId messengerId type replyCount likeCount message(asHtml:true)isLocked isSubscribed isLiked isPrivate siteUrl createdAt recipient{id name bannerImage avatar{medium large}}messenger{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}}}}""",
force = true
)
}
suspend fun getUpcomingAnime(id: String): List<Media> { suspend fun getUpcomingAnime(id: String): List<Media> {
val res = executeQuery<Query.MediaListCollection>( val res = executeQuery<Query.MediaListCollection>(
"""{MediaListCollection(userId:$id,type:ANIME){lists{name entries{media{id,isFavourite,title{userPreferred,romaji}coverImage{medium}nextAiringEpisode{timeUntilAiring}}}}}}""", """{MediaListCollection(userId:$id,type:ANIME){lists{name entries{media{id,isFavourite,title{userPreferred,romaji}coverImage{medium}nextAiringEpisode{timeUntilAiring}}}}}}""",

View file

@ -11,6 +11,8 @@ import ani.dantotsu.connections.discord.Discord
import ani.dantotsu.connections.mal.MAL import ani.dantotsu.connections.mal.MAL
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.AppUpdater
import ani.dantotsu.profile.User
import ani.dantotsu.profile.activity.ActivityItemBuilder
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
@ -100,6 +102,35 @@ class AnilistHomeViewModel : ViewModel() {
res["plannedManga"]?.let { mangaPlanned.postValue(it) } res["plannedManga"]?.let { mangaPlanned.postValue(it) }
res["recommendations"]?.let { recommendation.postValue(it) } res["recommendations"]?.let { recommendation.postValue(it) }
} }
private val userStatus: MutableLiveData<ArrayList<User>> =
MutableLiveData<ArrayList<User>>(null)
fun getUserStatus(): LiveData<ArrayList<User>> = userStatus
suspend fun initUserStatus() {
Anilist.query.getStatus()?.data?.page?.activities?.let { activities ->
val groupedActivities = activities
.filterNot { it.userId == Anilist.userid }
.sortedByDescending { ActivityItemBuilder.getDateTime(it.createdAt) }
.groupBy { it.userId }
val userList = groupedActivities.mapNotNull { (_, activities) ->
val user = activities.firstOrNull()?.user
user?.let {
User(
it.id,
it.name ?: "",
it.avatar?.medium,
it.bannerImage,
activity = activities
)
}
}.toMutableList()
userList.sortByDescending { user ->
user.activity.maxByOrNull { it.createdAt }?.createdAt
}
userStatus.postValue(ArrayList(userList))
}
}
suspend fun loadMain(context: FragmentActivity) { suspend fun loadMain(context: FragmentActivity) {
Anilist.getSavedToken() Anilist.getSavedToken()

View file

@ -174,7 +174,7 @@ data class Media(
// Notes for site moderators // Notes for site moderators
@SerialName("modNotes") var modNotes: String?, @SerialName("modNotes") var modNotes: String?,
) ) : java.io.Serializable
@Serializable @Serializable
data class MediaTitle( data class MediaTitle(
@ -189,7 +189,7 @@ data class MediaTitle(
// The currently authenticated users preferred title language. Default romaji for non-authenticated // The currently authenticated users preferred title language. Default romaji for non-authenticated
@SerialName("userPreferred") var userPreferred: String, @SerialName("userPreferred") var userPreferred: String,
) ): java.io.Serializable
@Serializable @Serializable
enum class MediaType { enum class MediaType {

View file

@ -69,7 +69,7 @@ data class User(
// The user's previously used names. // The user's previously used names.
// @SerialName("previousNames") var previousNames: List<UserPreviousName>?, // @SerialName("previousNames") var previousNames: List<UserPreviousName>?,
) ): java.io.Serializable
@Serializable @Serializable
data class UserOptions( data class UserOptions(

View file

@ -314,10 +314,37 @@ class HomeFragment : Fragment() {
binding.homeRecommendedEmpty, binding.homeRecommendedEmpty,
binding.homeRecommended binding.homeRecommended
) )
binding.homeUserStatusContainer.visibility = View.VISIBLE
binding.homeUserStatusProgressBar.visibility = View.VISIBLE
binding.homeUserStatusRecyclerView.visibility = View.GONE
binding.homeUserStatus.visibility = View.INVISIBLE
model.getUserStatus().observe(viewLifecycleOwner) {
binding.homeUserStatusRecyclerView.visibility = View.GONE
if (it != null) {
if (it.isNotEmpty()) {
binding.homeUserStatusRecyclerView.adapter = UserStatus(it)
binding.homeUserStatusRecyclerView.layoutManager = LinearLayoutManager(
requireContext(),
LinearLayoutManager.HORIZONTAL,
false
)
binding.homeUserStatusRecyclerView.visibility = View.VISIBLE
binding.homeUserStatusRecyclerView.layoutAnimation =
LayoutAnimationController(setSlideIn(), 0.25f)
} else {
binding.homeUserStatusContainer.visibility = View.GONE
}
binding.homeUserStatus.visibility = View.VISIBLE
binding.homeUserStatus.startAnimation(setSlideUp())
binding.homeUserStatusProgressBar.visibility = View.GONE
}
}
binding.homeUserAvatarContainer.startAnimation(setSlideUp()) binding.homeUserAvatarContainer.startAnimation(setSlideUp())
model.empty.observe(viewLifecycleOwner) { model.empty.observe(viewLifecycleOwner)
{
binding.homeDantotsuContainer.visibility = if (it == true) View.VISIBLE else View.GONE binding.homeDantotsuContainer.visibility = if (it == true) View.VISIBLE else View.GONE
(binding.homeDantotsuIcon.drawable as Animatable).start() (binding.homeDantotsuIcon.drawable as Animatable).start()
binding.homeDantotsuContainer.startAnimation(setSlideUp()) binding.homeDantotsuContainer.startAnimation(setSlideUp())
@ -348,7 +375,8 @@ class HomeFragment : Fragment() {
) )
val live = Refresh.activity.getOrPut(1) { MutableLiveData(false) } val live = Refresh.activity.getOrPut(1) { MutableLiveData(false) }
live.observe(viewLifecycleOwner) { live.observe(viewLifecycleOwner)
{
if (it) { if (it) {
scope.launch { scope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@ -362,6 +390,7 @@ class HomeFragment : Fragment() {
val homeLayoutShow: List<Boolean> = val homeLayoutShow: List<Boolean> =
PrefManager.getVal(PrefName.HomeLayoutShow) PrefManager.getVal(PrefName.HomeLayoutShow)
runBlocking { runBlocking {
model.initUserStatus()
model.initHomePage() model.initHomePage()
} }
(array.indices).forEach { i -> (array.indices).forEach { i ->

View file

@ -0,0 +1,73 @@
package ani.dantotsu.home
import android.os.Bundle
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updateLayoutParams
import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.ActivityStatusBinding
import ani.dantotsu.initActivity
import ani.dantotsu.others.getSerialized
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.home.status.data.StoryItem
import ani.dantotsu.home.status.listener.StoriesCallback
import ani.dantotsu.navBarHeight
import ani.dantotsu.profile.activity.ActivityItemBuilder
import ani.dantotsu.statusBarHeight
class StatusActivity : AppCompatActivity(), StoriesCallback {
private lateinit var activity: List<Activity>
private lateinit var binding: ActivityStatusBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ThemeManager(this).applyTheme()
initActivity(this)
binding = ActivityStatusBinding.inflate(layoutInflater)
setContentView(binding.root)
activity = intent.getSerialized("activity")!!
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = statusBarHeight
bottomMargin = navBarHeight
}
val storiesList = activity.map { StoryItem(
id = it.userId,
activityId = it.id,
mediaId = it.media?.id,
userName = it.user?.name,
userAvatar = it.user?.avatar?.large,
time = ActivityItemBuilder.getDateTime(it.createdAt),
info = "${it.user!!.name} ${it.status} ${
it.progress
?: it.media?.title?.userPreferred
}",
cover = it.media?.coverImage?.extraLarge,
banner = it.media?.bannerImage ?: it.media?.coverImage?.extraLarge,
likes = it.likeCount ?: 0,
likedBy = it.likes,
isLiked = it.isLiked == true
) }
binding.stories.setStoriesList(storiesList, this)
}
override fun onPause() {
super.onPause()
binding.stories.pause()
}
override fun onResume() {
super.onResume()
binding.stories.resume()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
binding.stories.resume()
} else {
binding.stories.pause()
}
}
override fun onStoriesEnd() {
finish()
}
}

View file

@ -0,0 +1,53 @@
package ani.dantotsu.home
import android.content.Intent
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.databinding.ItemUserStatusBinding
import ani.dantotsu.loadImage
import ani.dantotsu.profile.User
import ani.dantotsu.setAnimation
import java.io.Serializable
class UserStatus(private val user: ArrayList<User>) :
RecyclerView.Adapter< UserStatus.UsersViewHolder>() {
inner class UsersViewHolder(val binding: ItemUserStatusBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
ContextCompat.startActivity(
itemView.context,
Intent(
itemView.context,
StatusActivity::class.java
).putExtra("activity", user[bindingAdapterPosition].activity as Serializable),
null
)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UsersViewHolder {
return UsersViewHolder(
ItemUserStatusBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: UsersViewHolder, position: Int) {
val b = holder.binding
setAnimation(b.root.context, b.root)
val user = user[position]
b.profileUserAvatar.loadImage(user.pfp)
b.profileUserName.text = user.name
}
override fun getItemCount(): Int = user.size
}

View file

@ -0,0 +1,413 @@
package ani.dantotsu.home.status
import android.animation.Animator
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import androidx.webkit.internal.ApiFeature.T
import ani.dantotsu.R
import ani.dantotsu.blurImage
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.home.status.data.StoryItem
import ani.dantotsu.home.status.listener.StoriesCallback
import ani.dantotsu.loadImage
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.User
import ani.dantotsu.profile.UsersDialogFragment
import ani.dantotsu.snackString
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Calendar
class Stories @JvmOverloads
constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), View.OnTouchListener {
private lateinit var storiesList: List<StoryItem>
private lateinit var activ: FragmentActivity
private lateinit var loadingViewLayout: ConstraintLayout
private lateinit var leftTouchPanel: FrameLayout
private lateinit var rightTouchPanel: FrameLayout
private lateinit var statusUserContainer: LinearLayout
private lateinit var imageContentView: ImageView
private lateinit var loadingView: ProgressBar
private lateinit var activityLikeCount: TextView
private lateinit var activityLike: ImageView
private lateinit var userName: TextView
private lateinit var userAvatar: ImageView
private lateinit var time: TextView
private lateinit var infoText: TextView
private lateinit var coverImage: ImageView
private var storyDuration: String = "4"
private lateinit var animation: ObjectAnimator
private var storyIndex: Int = 1
private var userClicked: Boolean = false
private lateinit var storiesListener: StoriesCallback
private var oldStoryItem = StoryItem()
init {
initLayout()
}
@SuppressLint("ClickableViewAccessibility")
private fun initLayout() {
val inflater: LayoutInflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.fragment_status, this, false)
addView(view)
if (context is StoriesCallback)
storiesListener = context as StoriesCallback
loadingViewLayout = findViewById(R.id.progressBarContainer)
leftTouchPanel = findViewById(R.id.leftTouchPanel)
rightTouchPanel = findViewById(R.id.rightTouchPanel)
imageContentView = findViewById(R.id.contentImageView)
statusUserContainer = findViewById(R.id.statusUserContainer)
loadingView = findViewById(R.id.androidStoriesLoadingView)
coverImage = findViewById(R.id.coverImage)
userName = findViewById(R.id.statusUserName)
userAvatar = findViewById(R.id.statusUserAvatar)
time = findViewById(R.id.statusUserTime)
infoText = findViewById(R.id.infoText)
activityLikeCount = findViewById(R.id.activityLikeCount)
activityLike = findViewById(R.id.activityLike)
leftTouchPanel.setOnTouchListener(this)
rightTouchPanel.setOnTouchListener(this)
}
fun setStoriesList(storiesList: List<StoryItem>, activity: FragmentActivity) {
this.storiesList = storiesList
this.activ = activity
addLoadingViews(storiesList)
}
private fun addLoadingViews(storiesList: List<StoryItem>) {
var idCounter = 1
for (story in storiesList) {
val progressBar = ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal)
progressBar.visibility = View.VISIBLE
progressBar.id = idCounter
progressBar.tag = "story${idCounter++}"
val params = LayoutParams(0, LayoutParams.WRAP_CONTENT)
params.marginEnd = 5
params.marginStart = 5
loadingViewLayout.addView(progressBar, params)
}
val constraintSet = ConstraintSet()
constraintSet.clone(loadingViewLayout)
var counter = storiesList.size
for (story in storiesList) {
val progressBar = findViewWithTag<ProgressBar>("story${counter}")
if (progressBar != null) {
if (storiesList.size > 1) {
when (counter) {
storiesList.size -> {
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.END,
LayoutParams.PARENT_ID,
ConstraintSet.END
)
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.TOP,
LayoutParams.PARENT_ID,
ConstraintSet.TOP
)
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.START,
getId("story${counter - 1}"),
ConstraintSet.END
)
}
1 -> {
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.TOP,
LayoutParams.PARENT_ID,
ConstraintSet.TOP
)
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.START,
LayoutParams.PARENT_ID,
ConstraintSet.START
)
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.END,
getId("story${counter + 1}"),
ConstraintSet.START
)
}
else -> {
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.TOP,
LayoutParams.PARENT_ID,
ConstraintSet.TOP
)
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.START,
getId("story${counter - 1}"),
ConstraintSet.END
)
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.END,
getId("story${counter + 1}"),
ConstraintSet.START
)
}
}
} else {
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.END,
LayoutParams.PARENT_ID,
ConstraintSet.END
)
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.TOP,
LayoutParams.PARENT_ID,
ConstraintSet.TOP
)
constraintSet.connect(
getId("story${counter}"),
ConstraintSet.START,
LayoutParams.PARENT_ID,
ConstraintSet.START
)
}
}
counter--
}
constraintSet.applyTo(loadingViewLayout)
startShowContent()
}
private fun startShowContent() {
showStory()
}
private fun showStory() {
val progressBar = findViewWithTag<ProgressBar>("story${storyIndex}")
loadingView.visibility = View.VISIBLE
animation = ObjectAnimator.ofInt(progressBar, "progress", 0, 100)
animation.duration = secondsToMillis(storyDuration)
animation.interpolator = LinearInterpolator()
animation.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animator: Animator) {
}
override fun onAnimationEnd(animator: Animator) {
if (storyIndex - 1 <= storiesList.size) {
if (userClicked) {
userClicked = false
} else {
if (storyIndex < storiesList.size) {
storyIndex += 1
showStory()
} else {
// on stories end
loadingView.visibility = View.GONE
onStoriesCompleted()
}
}
} else {
// on stories end
loadingView.visibility = View.GONE
onStoriesCompleted()
}
}
override fun onAnimationCancel(animator: Animator) {
progressBar.progress = 100
}
override fun onAnimationRepeat(animator: Animator) {}
})
loadStory(storiesList[storyIndex - 1])
}
private fun getId(tag: String): Int {
return findViewWithTag<ProgressBar>(tag).id
}
private fun secondsToMillis(seconds: String): Long {
return (seconds.toLongOrNull() ?: 3).times(1000)
}
private fun resetProgressBar(storyIndex: Int) {
val currentProgressBar = findViewWithTag<ProgressBar>("story${storyIndex}")
val lastProgressBar = findViewWithTag<ProgressBar>("story${storyIndex - 1}")
currentProgressBar?.let {
it.progress = 0
}
lastProgressBar?.let {
it.progress = 0
}
}
private fun completeProgressBar(storyIndex: Int) {
val lastProgressBar = findViewWithTag<ProgressBar>("story${storyIndex}")
lastProgressBar?.let {
it.progress = 100
}
}
private var startClickTime = 0L
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
val maxClickDuration = 200
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
startClickTime = Calendar.getInstance().timeInMillis
animation.pause()
}
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()
}
}
} else {
//hold click occurred
animation.resume()
}
}
}
return true
}
private fun rightPanelTouch() {
if (storyIndex == storiesList.size) {
completeProgressBar(storyIndex)
onStoriesCompleted()
return
}
userClicked = true
animation.end()
if (storyIndex < storiesList.size)
storyIndex += 1
showStory()
}
private fun leftPanelTouch() {
userClicked = true
animation.end()
resetProgressBar(storyIndex)
if (storyIndex > 1)
storyIndex -= 1
showStory()
}
private fun onStoriesCompleted() {
if (::storiesListener.isInitialized)
storiesListener.onStoriesEnd()
}
fun pause() {
animation.pause()
}
fun resume() {
animation.resume()
}
private fun loadStory(story: StoryItem) {
loadingView.visibility = View.GONE
animation.start()
blurImage(imageContentView, story.banner)
userAvatar.loadImage(story.userAvatar)
coverImage.loadImage(story.cover)
userName.text = story.userName
time.text = story.time
infoText.text = story.info
statusUserContainer.setOnClickListener {
ContextCompat.startActivity(context, Intent(context, ProfileActivity::class.java).putExtra("userId", story.id), null)
}
coverImage.setOnClickListener{
ContextCompat.startActivity(context, Intent(context, MediaDetailsActivity::class.java).putExtra("mediaId", story.mediaId), null)
}
val likeColor = ContextCompat.getColor(context, R.color.yt_red)
val notLikeColor = ContextCompat.getColor(context, R.color.bg_opp)
activityLikeCount.text = story.likes.toString()
activityLike.setColorFilter(if (story.isLiked) likeColor else notLikeColor)
activityLike.setOnClickListener {
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
scope.launch {
val res = Anilist.query.toggleLike(story.activityId!!, "ACTIVITY")
withContext(Dispatchers.Main) {
if (res != null) {
if (story.isLiked) {
story.likes = story.likes.minus(1)
} else {
story.likes = story.likes.plus(1)
}
activityLikeCount.text = (story.likes ?: 0).toString()
story.isLiked = !story.isLiked
activityLike.setColorFilter(if (story.isLiked) likeColor else notLikeColor)
} else {
snackString("Failed to like activity")
}
}
}
}
val userList = arrayListOf<User>()
story.likedBy?.forEach { i ->
userList.add(User(i.id, i.name.toString(), i.avatar?.medium, i.bannerImage))
}
activityLike.setOnLongClickListener {
UsersDialogFragment().apply {
userList(userList)
show(activ.supportFragmentManager, "dialog")
}
true
}
oldStoryItem = story
}
}

View file

@ -0,0 +1,20 @@
package ani.dantotsu.home.status.data
import ani.dantotsu.connections.anilist.api.User
data class StoryItem(
val id : Int? = null,
val activityId : Int? = null,
val mediaId: Int? = null,
val userName: String? = null,
val userAvatar: String? = null,
val time: String? = null,
val info: String? = null,
val cover: String? = null,
val banner: String? = null,
var likes : Int = 0,
val likedBy: List<User>? = null,
var isLiked : Boolean = false
)

View file

@ -0,0 +1,6 @@
package ani.dantotsu.home.status.listener
interface StoriesCallback {
fun onStoriesEnd()
}

View file

@ -1,5 +1,6 @@
package ani.dantotsu.profile package ani.dantotsu.profile
import ani.dantotsu.connections.anilist.api.Activity
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
@ -14,6 +15,7 @@ data class User(
val progress: Int? = null, val progress: Int? = null,
val totalEpisodes: Int? = null, val totalEpisodes: Int? = null,
val nextAiringEpisode: Int? = null, val nextAiringEpisode: Int? = null,
val activity: List<Activity> = mutableListOf(),
) : java.io.Serializable { ) : java.io.Serializable {
companion object { companion object {
private const val serialVersionUID: Long = 1 private const val serialVersionUID: Long = 1

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ani.dantotsu.home.status.Stories
android:id="@+id/stories"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -309,7 +309,50 @@
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
<LinearLayout
android:id="@+id/homeUserStatusContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/homeUserStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:fontFamily="@font/poppins_bold"
android:padding="8dp"
android:text="@string/social"
android:textSize="16sp" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="100dp">
<ProgressBar
android:id="@+id/homeUserStatusProgressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="90dp" />
<ani.dantotsu.FadingEdgeRecyclerView
android:id="@+id/homeUserStatusRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:requiresFadingEdge="horizontal"
tools:itemCount="4"
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_user_status"
tools:orientation="horizontal" />
</FrameLayout>
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/homeContinueWatchingContainer" android:id="@+id/homeContinueWatchingContainer"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -0,0 +1,210 @@
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription">
<ImageView
android:id="@+id/contentImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop" />
<FrameLayout
android:id="@+id/leftTouchPanel"
android:layout_width="0dp"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_percent="0.5" />
<FrameLayout
android:id="@+id/rightTouchPanel"
android:layout_width="0dp"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintWidth_percent="0.5" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="MissingConstraints">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/progressBarContainer"
android:layout_width="match_parent"
android:layout_height="24dp"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/statusUserContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.card.MaterialCardView
android:id="@+id/statusUserAvatarContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_margin="12dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="64dp"
app:strokeColor="@color/transparent">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/statusUserAvatar"
android:layout_width="32dp"
android:layout_height="32dp"
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/statusUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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:textSize="16sp"
tools:ignore="HardcodedText,RtlSymmetry" />
<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="18sp"
tools:ignore="HardcodedText,RtlSymmetry" />
<TextView
android:id="@+id/statusUserTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/activityLike"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_round_favorite_24"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/activityLikeCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="@font/poppins_semi_bold"
android:textSize="15sp"
tools:text="12" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<androidx.cardview.widget.CardView
android:id="@+id/itemCompactCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:translationZ="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="4dp"
android:layout_marginBottom="124dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/coverImage"
android:layout_width="172dp"
android:layout_height="256dp"
android:scaleType="centerCrop"
android:transitionName="mediaCover"
app:shapeAppearanceOverlay="@style/roundedImageView"
tools:ignore="ContentDescription,ImageContrastCheck"
tools:srcCompat="@tools:sample/backgrounds/scenic" />
</androidx.cardview.widget.CardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="124dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:backgroundTint="@color/transparent"
app:cardCornerRadius="124dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/itemCompactCard"
app:strokeColor="@color/transparent">
<TextView
android:id="@+id/infoText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorSurfaceVariant"
android:ellipsize="marquee"
android:paddingHorizontal="12dp"
android:fontFamily="@font/poppins_semi_bold"
android:lineSpacingExtra="-8sp"
android:maxLines="3"
android:textAlignment="center"
android:textColor="@color/bg_opp"
android:textSize="24dp"
android:transitionName="mediaTitle"
tools:ignore="SpUsage"
tools:text="@string/slogan" />
</com.google.android.material.card.MaterialCardView>
<ProgressBar
android:id="@+id/androidStoriesLoadingView"
android:layout_width="40dp"
android:layout_height="40dp"
android:indeterminate="true"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,37 @@
<?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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<com.google.android.material.card.MaterialCardView
android:id="@+id/profileUserAvatarContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="@color/bg_white"
app:cardCornerRadius="124dp">
<ImageView
android:id="@+id/profileUserAvatar"
android:layout_width="64dp"
android:layout_height="64dp"
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_horizontal|center_vertical"
android:ellipsize="end"
android:text="@string/username"
android:textColor="?attr/colorOnBackground"
android:textSize="14sp" />
</LinearLayout>