diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 203d7307..85d3b9c6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -139,6 +139,9 @@ + diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt index b39aeebc..5c4e1b1c 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -1637,7 +1637,13 @@ Page(page:$page,perPage:50) { force = true ) } - + suspend fun getStatus( + ): FeedResponse? { + return executeQuery( + """{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 { val res = executeQuery( """{MediaListCollection(userId:$id,type:ANIME){lists{name entries{media{id,isFavourite,title{userPreferred,romaji}coverImage{medium}nextAiringEpisode{timeUntilAiring}}}}}}""", diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt index 11d5cda1..9874ce50 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt @@ -11,6 +11,8 @@ import ani.dantotsu.connections.discord.Discord import ani.dantotsu.connections.mal.MAL import ani.dantotsu.media.Media 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.PrefName import ani.dantotsu.snackString @@ -100,6 +102,35 @@ class AnilistHomeViewModel : ViewModel() { res["plannedManga"]?.let { mangaPlanned.postValue(it) } res["recommendations"]?.let { recommendation.postValue(it) } } + private val userStatus: MutableLiveData> = + MutableLiveData>(null) + + fun getUserStatus(): LiveData> = 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) { Anilist.getSavedToken() diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt index da2a989e..471b130d 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt @@ -174,7 +174,7 @@ data class Media( // Notes for site moderators @SerialName("modNotes") var modNotes: String?, -) +) : java.io.Serializable @Serializable data class MediaTitle( @@ -189,7 +189,7 @@ data class MediaTitle( // The currently authenticated users preferred title language. Default romaji for non-authenticated @SerialName("userPreferred") var userPreferred: String, -) +): java.io.Serializable @Serializable enum class MediaType { diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt index f6668fc4..9117c144 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt @@ -69,7 +69,7 @@ data class User( // The user's previously used names. // @SerialName("previousNames") var previousNames: List?, -) +): java.io.Serializable @Serializable data class UserOptions( diff --git a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt index 20a5d163..a496d80e 100644 --- a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt @@ -314,10 +314,37 @@ class HomeFragment : Fragment() { binding.homeRecommendedEmpty, 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()) - model.empty.observe(viewLifecycleOwner) { + model.empty.observe(viewLifecycleOwner) + { binding.homeDantotsuContainer.visibility = if (it == true) View.VISIBLE else View.GONE (binding.homeDantotsuIcon.drawable as Animatable).start() binding.homeDantotsuContainer.startAnimation(setSlideUp()) @@ -348,7 +375,8 @@ class HomeFragment : Fragment() { ) val live = Refresh.activity.getOrPut(1) { MutableLiveData(false) } - live.observe(viewLifecycleOwner) { + live.observe(viewLifecycleOwner) + { if (it) { scope.launch { withContext(Dispatchers.IO) { @@ -362,6 +390,7 @@ class HomeFragment : Fragment() { val homeLayoutShow: List = PrefManager.getVal(PrefName.HomeLayoutShow) runBlocking { + model.initUserStatus() model.initHomePage() } (array.indices).forEach { i -> diff --git a/app/src/main/java/ani/dantotsu/home/StatusActivity.kt b/app/src/main/java/ani/dantotsu/home/StatusActivity.kt new file mode 100644 index 00000000..cee888f2 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/home/StatusActivity.kt @@ -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 + 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 { + 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() + } + +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/home/UserStatus.kt b/app/src/main/java/ani/dantotsu/home/UserStatus.kt new file mode 100644 index 00000000..61351bde --- /dev/null +++ b/app/src/main/java/ani/dantotsu/home/UserStatus.kt @@ -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) : + 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 +} diff --git a/app/src/main/java/ani/dantotsu/home/status/Stories.kt b/app/src/main/java/ani/dantotsu/home/status/Stories.kt new file mode 100644 index 00000000..bb6c5095 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/home/status/Stories.kt @@ -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 + 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, activity: FragmentActivity) { + this.storiesList = storiesList + this.activ = activity + addLoadingViews(storiesList) + } + + private fun addLoadingViews(storiesList: List) { + 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("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("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(tag).id + } + + private fun secondsToMillis(seconds: String): Long { + return (seconds.toLongOrNull() ?: 3).times(1000) + } + + private fun resetProgressBar(storyIndex: Int) { + val currentProgressBar = findViewWithTag("story${storyIndex}") + val lastProgressBar = findViewWithTag("story${storyIndex - 1}") + currentProgressBar?.let { + it.progress = 0 + } + lastProgressBar?.let { + it.progress = 0 + } + } + + private fun completeProgressBar(storyIndex: Int) { + val lastProgressBar = findViewWithTag("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() + 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 + } + + + +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/home/status/data/StoryItem.kt b/app/src/main/java/ani/dantotsu/home/status/data/StoryItem.kt new file mode 100644 index 00000000..04fd93a1 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/home/status/data/StoryItem.kt @@ -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? = null, + var isLiked : Boolean = false +) + + diff --git a/app/src/main/java/ani/dantotsu/home/status/listener/StoriesCallback.kt b/app/src/main/java/ani/dantotsu/home/status/listener/StoriesCallback.kt new file mode 100644 index 00000000..0ba0251b --- /dev/null +++ b/app/src/main/java/ani/dantotsu/home/status/listener/StoriesCallback.kt @@ -0,0 +1,6 @@ +package ani.dantotsu.home.status.listener + +interface StoriesCallback { + + fun onStoriesEnd() +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/User.kt b/app/src/main/java/ani/dantotsu/profile/User.kt index a16c3329..3b231060 100644 --- a/app/src/main/java/ani/dantotsu/profile/User.kt +++ b/app/src/main/java/ani/dantotsu/profile/User.kt @@ -1,5 +1,6 @@ package ani.dantotsu.profile +import ani.dantotsu.connections.anilist.api.Activity import kotlinx.serialization.Serializable @Serializable @@ -14,6 +15,7 @@ data class User( val progress: Int? = null, val totalEpisodes: Int? = null, val nextAiringEpisode: Int? = null, + val activity: List = mutableListOf(), ) : java.io.Serializable { companion object { private const val serialVersionUID: Long = 1 diff --git a/app/src/main/res/layout/activity_status.xml b/app/src/main/res/layout/activity_status.xml new file mode 100644 index 00000000..e26c5708 --- /dev/null +++ b/app/src/main/res/layout/activity_status.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 0204115b..a23d4581 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -309,7 +309,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_user_status.xml b/app/src/main/res/layout/item_user_status.xml new file mode 100644 index 00000000..d1fd06aa --- /dev/null +++ b/app/src/main/res/layout/item_user_status.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + \ No newline at end of file