From 500de4e45e90b576a03ee872c7060827ee673987 Mon Sep 17 00:00:00 2001 From: rebelonion <87634197+rebelonion@users.noreply.github.com> Date: Sat, 2 Mar 2024 04:54:02 -0600 Subject: [PATCH] feat: statistics (wip) --- app/src/main/java/ani/dantotsu/Functions.kt | 85 ++++ .../dantotsu/media/comments/CommentItem.kt | 12 +- .../media/comments/CommentsFragment.kt | 79 +--- .../ani/dantotsu/profile/ProfileActivity.kt | 107 +++++- .../ani/dantotsu/profile/ProfileFragment.kt | 50 +++ .../dantotsu/profile/StatisticsActivity.kt | 4 - .../ani/dantotsu/profile/StatsFragment.kt | 157 ++++++++ .../main/res/drawable/ic_round_person_24.xml | 2 +- app/src/main/res/drawable/ic_stats_24.xml | 10 + app/src/main/res/layout/activity_profile.xml | 363 +++++++----------- app/src/main/res/layout/fragment_profile.xml | 156 ++++++++ .../main/res/layout/fragment_statistics.xml | 21 +- 12 files changed, 690 insertions(+), 356 deletions(-) create mode 100644 app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt delete mode 100644 app/src/main/java/ani/dantotsu/profile/StatisticsActivity.kt create mode 100644 app/src/main/java/ani/dantotsu/profile/StatsFragment.kt create mode 100644 app/src/main/res/drawable/ic_stats_24.xml create mode 100644 app/src/main/res/layout/fragment_profile.xml diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt index 15f873ea..d499d8e2 100644 --- a/app/src/main/java/ani/dantotsu/Functions.kt +++ b/app/src/main/java/ani/dantotsu/Functions.kt @@ -17,6 +17,7 @@ import android.content.res.Configuration import android.content.res.Resources.getSystem import android.graphics.Bitmap import android.graphics.Color +import android.graphics.drawable.Drawable import android.media.MediaScannerConnection import android.net.ConnectivityManager import android.net.NetworkCapabilities.* @@ -59,8 +60,15 @@ import ani.dantotsu.settings.saving.internal.PreferenceKeystore import ani.dantotsu.settings.saving.internal.PreferenceKeystore.Companion.generateSalt import ani.dantotsu.subcriptions.NotificationClickReceiver import com.bumptech.glide.Glide +import com.bumptech.glide.RequestBuilder +import com.bumptech.glide.RequestManager +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade +import com.bumptech.glide.load.resource.gif.GifDrawable +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomsheet.BottomSheetBehavior @@ -68,6 +76,17 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.internal.ViewUtils import com.google.android.material.snackbar.Snackbar import eu.kanade.tachiyomi.data.notification.Notifications +import io.noties.markwon.AbstractMarkwonPlugin +import io.noties.markwon.Markwon +import io.noties.markwon.MarkwonConfiguration +import io.noties.markwon.SoftBreakAddsNewLinePlugin +import io.noties.markwon.ext.strikethrough.StrikethroughPlugin +import io.noties.markwon.ext.tables.TablePlugin +import io.noties.markwon.ext.tasklist.TaskListPlugin +import io.noties.markwon.html.HtmlPlugin +import io.noties.markwon.html.TagHandlerNoOp +import io.noties.markwon.image.AsyncDrawable +import io.noties.markwon.image.glide.GlideImagesPlugin import kotlinx.coroutines.* import nl.joery.animatedbottombar.AnimatedBottomBar import uy.kohesive.injekt.Injekt @@ -1079,4 +1098,70 @@ fun logToFile(context: Context, message: String) { val file = File(externalFilesDir, "notifications.log") file.appendText(message) file.appendText("\n") +} + +/** + * Builds the markwon instance with all the plugins + * @return the markwon instance + */ +fun buildMarkwon(activity: Activity, userInputContent: Boolean = true): Markwon { + val markwon = Markwon.builder(activity) + .usePlugin(object : AbstractMarkwonPlugin() { + override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { + builder.linkResolver { view, link -> + copyToClipboard(link, true) + } + } + }) + + .usePlugin(SoftBreakAddsNewLinePlugin.create()) + .usePlugin(StrikethroughPlugin.create()) + .usePlugin(TablePlugin.create(activity)) + .usePlugin(TaskListPlugin.create(activity)) + .usePlugin(HtmlPlugin.create { plugin -> + if (!userInputContent) { + plugin.addHandler( + TagHandlerNoOp.create("h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre", "a") + ) + } + }) + .usePlugin(GlideImagesPlugin.create(object : GlideImagesPlugin.GlideStore { + + private val requestManager: RequestManager = + Glide.with(activity).apply { + addDefaultRequestListener(object : RequestListener { + override fun onResourceReady( + resource: Any, + model: Any, + target: Target, + dataSource: DataSource, + isFirstResource: Boolean + ): Boolean { + if (resource is GifDrawable) { + resource.start() + } + return false + } + + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target, + isFirstResource: Boolean + ): Boolean { + return false + } + }) + } + + override fun load(drawable: AsyncDrawable): RequestBuilder { + return requestManager.load(drawable.destination) + } + + override fun cancel(target: Target<*>) { + requestManager.clear(target) + } + })) + .build() + return markwon } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt index 246145e7..90419d87 100644 --- a/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt +++ b/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt @@ -10,12 +10,8 @@ import ani.dantotsu.R import ani.dantotsu.connections.comments.Comment import ani.dantotsu.connections.comments.CommentsAPI import ani.dantotsu.copyToClipboard -import ani.dantotsu.currActivity -import ani.dantotsu.currContext import ani.dantotsu.databinding.ItemCommentsBinding import ani.dantotsu.loadImage -import ani.dantotsu.media.user.ListActivity -import ani.dantotsu.openLinkInBrowser import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.snackString import com.xwray.groupie.GroupieAdapter @@ -56,7 +52,7 @@ class CommentItem(val comment: Comment, @SuppressLint("SetTextI18n") override fun bind(viewBinding: ItemCommentsBinding, position: Int) { binding = viewBinding - viewBinding.commentRepliesList.layoutManager = LinearLayoutManager(currActivity()) + viewBinding.commentRepliesList.layoutManager = LinearLayoutManager(commentsFragment.activity) viewBinding.commentRepliesList.adapter = adapter val isUserComment = CommentsAPI.userId == comment.userId val node = markwon.parse(comment.content) @@ -101,7 +97,7 @@ class CommentItem(val comment: Comment, viewBinding.commentUserName.setOnClickListener { ContextCompat.startActivity( - currContext()!!, Intent(currContext()!!, ProfileActivity::class.java) + commentsFragment.activity, Intent(commentsFragment.activity, ProfileActivity::class.java) .putExtra("userId", comment.userId.toInt()) .putExtra("username","[${levelColor.second}]"), null ) @@ -215,12 +211,12 @@ class CommentItem(val comment: Comment, } fun replying(isReplying: Boolean) { - binding?.commentReply?.text = if (isReplying) currActivity()!!.getString(R.string.cancel) else "Reply" + binding?.commentReply?.text = if (isReplying) commentsFragment.activity.getString(R.string.cancel) else "Reply" this.isReplying = isReplying } fun editing(isEditing: Boolean) { - binding?.commentEdit?.text = if (isEditing) currActivity()!!.getString(R.string.cancel) else currActivity()!!.getString(R.string.edit) + binding?.commentEdit?.text = if (isEditing) commentsFragment.activity.getString(R.string.cancel) else commentsFragment.activity.getString(R.string.edit) this.isEditing = isEditing } diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt index 64b968c1..b98d3a5e 100644 --- a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt @@ -22,6 +22,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import ani.dantotsu.R +import ani.dantotsu.buildMarkwon import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.comments.Comment import ani.dantotsu.connections.comments.CommentResponse @@ -100,7 +101,7 @@ class CommentsFragment : Fragment() { this.mediaId = mediaId backgroundColor = (binding.root.background as? ColorDrawable)?.color ?: 0 - val markwon = buildMarkwon() + val markwon = buildMarkwon(activity) binding.commentUserAvatar.loadImage(Anilist.avatar) val markwonEditor = MarkwonEditor.create(markwon) @@ -235,7 +236,7 @@ class CommentsFragment : Fragment() { section.add( CommentItem( comment, - buildMarkwon(), + buildMarkwon(activity), section, this@CommentsFragment, backgroundColor, @@ -386,7 +387,7 @@ class CommentsFragment : Fragment() { section.add( CommentItem( it, - buildMarkwon(), + buildMarkwon(activity), section, this@CommentsFragment, backgroundColor, @@ -416,7 +417,7 @@ class CommentsFragment : Fragment() { section.add( CommentItem( comment, - buildMarkwon(), + buildMarkwon(activity), section, this@CommentsFragment, backgroundColor, @@ -539,7 +540,7 @@ class CommentsFragment : Fragment() { if (depth >= comment.MAX_DEPTH) comment.registerSubComment(it.commentId) val newCommentItem = CommentItem( it, - buildMarkwon(), + buildMarkwon(activity), section, this@CommentsFragment, backgroundColor, @@ -664,7 +665,7 @@ class CommentsFragment : Fragment() { if (commentWithInteraction!!.commentDepth + 1 > commentWithInteraction!!.MAX_DEPTH) 0 else section.itemCount, CommentItem( it, - buildMarkwon(), + buildMarkwon(activity), section, this@CommentsFragment, backgroundColor, @@ -676,7 +677,7 @@ class CommentsFragment : Fragment() { 0, CommentItem( it, - buildMarkwon(), + buildMarkwon(activity), section, this@CommentsFragment, backgroundColor, @@ -686,68 +687,4 @@ class CommentsFragment : Fragment() { } } } - - /** - * Builds the markwon instance with all the plugins - * @return the markwon instance - */ - private fun buildMarkwon(): Markwon { - val markwon = Markwon.builder(activity) - .usePlugin(object : AbstractMarkwonPlugin() { - override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { - builder.linkResolver { view, link -> - copyToClipboard(link, true) - } - } - }) - - .usePlugin(SoftBreakAddsNewLinePlugin.create()) - .usePlugin(StrikethroughPlugin.create()) - .usePlugin(TablePlugin.create(activity)) - .usePlugin(TaskListPlugin.create(activity)) - .usePlugin(HtmlPlugin.create { plugin -> - plugin.addHandler( - TagHandlerNoOp.create("h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre", "a") - ) - }) - .usePlugin(GlideImagesPlugin.create(object : GlideImagesPlugin.GlideStore { - - private val requestManager: RequestManager = - Glide.with(this@CommentsFragment).apply { - addDefaultRequestListener(object : RequestListener { - override fun onResourceReady( - resource: Any, - model: Any, - target: Target, - dataSource: DataSource, - isFirstResource: Boolean - ): Boolean { - if (resource is GifDrawable) { - resource.start() - } - return false - } - - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target, - isFirstResource: Boolean - ): Boolean { - return false - } - }) - } - - override fun load(drawable: AsyncDrawable): RequestBuilder { - return requestManager.load(drawable.destination) - } - - override fun cancel(target: Target<*>) { - requestManager.clear(target) - } - })) - .build() - return markwon - } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt index 3feed339..5c77d40f 100644 --- a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt @@ -6,58 +6,123 @@ import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.viewpager2.adapter.FragmentStateAdapter +import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist +import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.databinding.ActivityProfileBinding +import ani.dantotsu.initActivity import ani.dantotsu.loadImage +import ani.dantotsu.media.Media +import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.user.ListActivity +import ani.dantotsu.others.ImageViewDialog +import ani.dantotsu.snackString +import ani.dantotsu.themes.ThemeManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import nl.joery.animatedbottombar.AnimatedBottomBar class ProfileActivity : AppCompatActivity(){ private lateinit var binding: ActivityProfileBinding + private var selected: Int = 0 + private lateinit var tabLayout: AnimatedBottomBar @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + ThemeManager(this).applyTheme() + initActivity(this) binding = ActivityProfileBinding.inflate(layoutInflater) setContentView(binding.root) + tabLayout = binding.typeTab + val profileTab = tabLayout.createTab(R.drawable.ic_round_person_24, "Profile") + val statsTab = tabLayout.createTab(R.drawable.ic_stats_24, "Stats") + tabLayout.addTab(profileTab) + tabLayout.addTab(statsTab) + tabLayout.visibility = View.GONE + binding.mediaViewPager.isUserInputEnabled = false + + lifecycleScope.launch(Dispatchers.IO) { val userid = intent.getIntExtra("userId", 0) val respond = Anilist.query.getUserProfile(userid) - val user = respond?.data?.user ?: return@launch - val userLevel = intent.getStringExtra("username") + val user = respond?.data?.user + if (user == null) { + snackString("User not found") + finish() + return@launch + } withContext(Dispatchers.Main) { + binding.mediaViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, user, this@ProfileActivity) + tabLayout.visibility = View.VISIBLE + tabLayout.selectTabAt(selected) + tabLayout.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener { + override fun onTabSelected( + lastIndex: Int, + lastTab: AnimatedBottomBar.Tab?, + newIndex: Int, + newTab: AnimatedBottomBar.Tab + ) { + selected = newIndex + binding.mediaViewPager.setCurrentItem(selected, true) + } + }) + val userLevel = intent.getStringExtra("username")?: "" + binding.profileProgressBar.visibility = View.GONE binding.profileBannerImage.loadImage(user.bannerImage) binding.profileUserAvatar.loadImage(user.avatar?.medium) binding.profileUserName.text = "${user.name} $userLevel" - binding.profileUserInfo.text = user.about - binding.profileAnimeList.setOnClickListener { - ContextCompat.startActivity( - this@ProfileActivity, Intent(this@ProfileActivity, ListActivity::class.java) - .putExtra("anime", true) - .putExtra("userId", user.id) - .putExtra("username", user.name), null - ) - } - binding.profileMangaList.setOnClickListener { - ContextCompat.startActivity( - this@ProfileActivity, Intent(this@ProfileActivity, ListActivity::class.java) - .putExtra("anime", false) - .putExtra("userId", user.id) - .putExtra("username", user.name), null - ) - } binding.profileUserEpisodesWatched.text = user.statistics.anime.episodesWatched.toString() binding.profileUserChaptersRead.text = user.statistics.manga.chaptersRead.toString() - binding.profileAnimeListImage.loadImage("https://bit.ly/31bsIHq") - binding.profileMangaListImage.loadImage("https://bit.ly/2ZGfcuG") + binding.profileBannerImage.loadImage(user.bannerImage) + binding.profileBannerImage.setOnLongClickListener { + ImageViewDialog.newInstance( + this@ProfileActivity, + user.name + " [Banner]", + user.bannerImage + ) + } + binding.profileUserAvatar.loadImage(user.avatar?.medium) + binding.profileUserAvatar.setOnLongClickListener { + ImageViewDialog.newInstance( + this@ProfileActivity, + user.name + " [Avatar]", + user.avatar?.medium + ) + } } } } + override fun onResume() { + if (this::tabLayout.isInitialized) { + tabLayout.selectTabAt(selected) + } + super.onResume() + } + + private class ViewPagerAdapter( + fragmentManager: FragmentManager, + lifecycle: Lifecycle, + private val user: Query.UserProfile, + private val activity: ProfileActivity + ) : + FragmentStateAdapter(fragmentManager, lifecycle) { + + override fun getItemCount(): Int = 2 + override fun createFragment(position: Int): Fragment = when (position) { + 0 -> ProfileFragment(user, activity) + 1 -> StatsFragment(user, activity) + else -> ProfileFragment(user, activity) + } + } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt new file mode 100644 index 00000000..504bce57 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt @@ -0,0 +1,50 @@ +package ani.dantotsu.profile + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import ani.dantotsu.buildMarkwon +import ani.dantotsu.connections.anilist.api.Query +import ani.dantotsu.databinding.FragmentProfileBinding +import ani.dantotsu.loadImage +import ani.dantotsu.media.user.ListActivity + +class ProfileFragment(private val user: Query.UserProfile, private val activity: ProfileActivity): Fragment() { + lateinit var binding: FragmentProfileBinding + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentProfileBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val markwon = buildMarkwon(activity, false) + markwon.setMarkdown(binding.profileUserInfo, user.about?:"") + binding.profileAnimeList.setOnClickListener { + ContextCompat.startActivity( + activity, Intent(activity, ListActivity::class.java) + .putExtra("anime", true) + .putExtra("userId", user.id) + .putExtra("username", user.name), null + ) + } + binding.profileMangaList.setOnClickListener { + ContextCompat.startActivity( + activity, Intent(activity, ListActivity::class.java) + .putExtra("anime", false) + .putExtra("userId", user.id) + .putExtra("username", user.name), null + ) + } + binding.profileAnimeListImage.loadImage("https://bit.ly/31bsIHq") + binding.profileMangaListImage.loadImage("https://bit.ly/2ZGfcuG") + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/StatisticsActivity.kt b/app/src/main/java/ani/dantotsu/profile/StatisticsActivity.kt deleted file mode 100644 index cbe625b4..00000000 --- a/app/src/main/java/ani/dantotsu/profile/StatisticsActivity.kt +++ /dev/null @@ -1,4 +0,0 @@ -package ani.dantotsu.profile - -class StatisticsActivity { -} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt new file mode 100644 index 00000000..153f60cf --- /dev/null +++ b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt @@ -0,0 +1,157 @@ +package ani.dantotsu.profile + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import ani.dantotsu.R +import ani.dantotsu.connections.anilist.Anilist +import ani.dantotsu.connections.anilist.api.Query +import ani.dantotsu.databinding.FragmentStatisticsBinding +import com.github.aachartmodel.aainfographics.aachartcreator.AAChartAlignType +import com.github.aachartmodel.aainfographics.aachartcreator.AAChartLayoutType +import com.github.aachartmodel.aainfographics.aachartcreator.AAChartModel +import com.github.aachartmodel.aainfographics.aachartcreator.AAChartStackingType +import com.github.aachartmodel.aainfographics.aachartcreator.AAChartType +import com.github.aachartmodel.aainfographics.aachartcreator.AAChartVerticalAlignType +import com.github.aachartmodel.aainfographics.aachartcreator.AAChartZoomType +import com.github.aachartmodel.aainfographics.aachartcreator.AADataElement +import com.github.aachartmodel.aainfographics.aachartcreator.AAOptions +import com.github.aachartmodel.aainfographics.aachartcreator.AASeriesElement +import com.github.aachartmodel.aainfographics.aachartcreator.aa_toAAOptions +import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAChart +import com.github.aachartmodel.aainfographics.aaoptionsmodel.AALang +import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAPosition +import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAScrollablePlotArea +import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAStyle +import com.github.aachartmodel.aainfographics.aatools.AAColor +import com.github.aachartmodel.aainfographics.aatools.AAGradientColor +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import nl.joery.animatedbottombar.AnimatedBottomBar + +class StatsFragment(private val user: Query.UserProfile, private val activity: ProfileActivity) : + Fragment() { + private lateinit var binding: FragmentStatisticsBinding + private var selected: Int = 0 + private lateinit var tabLayout: AnimatedBottomBar + private var stats: Query.StatisticsResponse? = null + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentStatisticsBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + tabLayout = binding.typeTab + val animeTab = tabLayout.createTab(R.drawable.ic_round_movie_filter_24, "Anime") + val mangaTab = tabLayout.createTab(R.drawable.ic_round_menu_book_24, "Manga") + tabLayout.addTab(animeTab) + tabLayout.addTab(mangaTab) + + tabLayout.visibility = View.GONE + activity.lifecycleScope.launch { + stats = Anilist.query.getUserStatistics(user.id) + withContext(Dispatchers.Main) { + tabLayout.visibility = View.VISIBLE + tabLayout.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener { + override fun onTabSelected( + lastIndex: Int, + lastTab: AnimatedBottomBar.Tab?, + newIndex: Int, + newTab: AnimatedBottomBar.Tab + ) { + selected = newIndex + when (newIndex) { + 0 -> loadAnimeStats() + 1 -> loadMangaStats() + } + } + }) + tabLayout.selectTabAt(selected) + loadAnimeStats() + } + } + } + + override fun onResume() { + if (this::tabLayout.isInitialized) { + tabLayout.selectTabAt(selected) + } + super.onResume() + } + + private fun loadAnimeStats() { + val formatChartModel = getFormatChartModel(true) + if (formatChartModel != null) { + val aaOptions = buildOptions(formatChartModel) + binding.formatChartView.aa_drawChartWithChartOptions(aaOptions) + } + } + + private fun loadMangaStats() { + val formatChartModel = getFormatChartModel(false) + if (formatChartModel != null) { + val aaOptions = buildOptions(formatChartModel) + binding.formatChartView.aa_drawChartWithChartOptions(aaOptions) + } + } + + private fun buildOptions(aaChartModel: AAChartModel): AAOptions { + val aaOptions = aaChartModel.aa_toAAOptions() + aaOptions.tooltip?.apply { + backgroundColor(AAGradientColor.PurpleLake) + .style(AAStyle.style(AAColor.White)) + } + aaOptions.chart?.zoomType = "xy" + aaOptions.chart?.pinchType = "xy" + aaOptions.legend?.apply { + enabled(true) + .verticalAlign(AAChartVerticalAlignType.Top) + .layout(AAChartLayoutType.Vertical) + .align(AAChartAlignType.Right) + .itemMarginTop(10f) + .labelFormat = "{name}: {y}" + } + aaOptions.plotOptions?.series?.connectNulls(true) + return aaOptions + } + + private fun getFormatChartModel(anime: Boolean): AAChartModel? { + val fotmatTypes: List = if (anime) { + stats?.data?.user?.statistics?.anime?.formats?.map { it.format } ?: emptyList() + } else { + stats?.data?.user?.statistics?.manga?.formats?.map { it.format } ?: emptyList() + } + val formatCount: List = if (anime) { + stats?.data?.user?.statistics?.anime?.formats?.map { it.count } ?: emptyList() + } else { + stats?.data?.user?.statistics?.manga?.formats?.map { it.count } ?: emptyList() + } + if (fotmatTypes.isEmpty() || formatCount.isEmpty()) + return null + return AAChartModel() + .chartType(AAChartType.Pie) + .title("Format") + .zoomType(AAChartZoomType.XY) + .dataLabelsEnabled(true) + .series(getElements(fotmatTypes, formatCount)) + } + + private fun getElements(types: List, counts: List): Array { + val elements = AASeriesElement() + val dataElements = mutableListOf() + for (i in types.indices) { + dataElements.add(AADataElement().name(types[i]).y(counts[i])) + } + return arrayOf(elements.data(dataElements.toTypedArray())) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_round_person_24.xml b/app/src/main/res/drawable/ic_round_person_24.xml index 4cecf074..1191a1e8 100644 --- a/app/src/main/res/drawable/ic_round_person_24.xml +++ b/app/src/main/res/drawable/ic_round_person_24.xml @@ -1,7 +1,7 @@ + + diff --git a/app/src/main/res/layout/activity_profile.xml b/app/src/main/res/layout/activity_profile.xml index 79d2c796..36aa8560 100644 --- a/app/src/main/res/layout/activity_profile.xml +++ b/app/src/main/res/layout/activity_profile.xml @@ -1,10 +1,10 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> + - - - - - - - - - - + android:layout_height="wrap_content" + android:layout_marginBottom="72dp"> + android:orientation="vertical"> - + + + + + + + + + android:layout_marginStart="18dp" + android:layout_marginTop="-32dp" + android:orientation="vertical" + tools:visibility="visible"> + + + + + + + + + + + + + + + + + + + + android:orientation="vertical" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - - + - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml new file mode 100644 index 00000000..ff24e877 --- /dev/null +++ b/app/src/main/res/layout/fragment_profile.xml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_statistics.xml b/app/src/main/res/layout/fragment_statistics.xml index 788e5494..f40f3b7b 100644 --- a/app/src/main/res/layout/fragment_statistics.xml +++ b/app/src/main/res/layout/fragment_statistics.xml @@ -1,13 +1,10 @@ - - - + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical"> - - - \ No newline at end of file