PixelDroid-App-Android/app/src/main/java/org/pixeldroid/app/profile/ProfileActivity.kt

361 lines
14 KiB
Kotlin

package org.pixeldroid.app.profile
import android.content.Intent
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.launch
import org.pixeldroid.app.R
import org.pixeldroid.app.databinding.ActivityProfileBinding
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedFeedFragment
import org.pixeldroid.app.posts.parseHTMLText
import org.pixeldroid.app.utils.BaseActivity
import org.pixeldroid.app.utils.api.PixelfedAPI
import org.pixeldroid.app.utils.api.objects.Account
import org.pixeldroid.app.utils.api.objects.FeedContent
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
import org.pixeldroid.app.utils.db.updateUserInfoDb
import org.pixeldroid.app.utils.setProfileImageFromURL
import retrofit2.HttpException
import java.io.IOException
class ProfileActivity : BaseActivity() {
private lateinit var domain : String
private lateinit var accountId : String
private lateinit var binding: ActivityProfileBinding
private var user: UserDatabaseEntity? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityProfileBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.topBar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
user = db.userDao().getActiveUser()
domain = user?.instance_uri.orEmpty()
// Set profile according to given account
val account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
accountId = account?.id ?: user!!.user_id
val tabs = createProfileTabs(account)
setupTabs(tabs)
setContent(account)
}
private fun createProfileTabs(account: Account?): Array<UncachedFeedFragment<FeedContent>> {
val profileFeedFragment = ProfileFeedFragment()
profileFeedFragment.arguments = Bundle().apply {
putSerializable(Account.ACCOUNT_TAG, account)
putSerializable(ProfileFeedFragment.PROFILE_GRID, false)
putSerializable(ProfileFeedFragment.BOOKMARKS, false)
}
val profileGridFragment = ProfileFeedFragment()
profileGridFragment.arguments = Bundle().apply {
putSerializable(Account.ACCOUNT_TAG, account)
putSerializable(ProfileFeedFragment.PROFILE_GRID, true)
putSerializable(ProfileFeedFragment.BOOKMARKS, false)
}
val profileCollectionsFragment = ProfileFeedFragment()
profileCollectionsFragment.arguments = Bundle().apply {
putSerializable(Account.ACCOUNT_TAG, account)
putSerializable(ProfileFeedFragment.PROFILE_GRID, true)
putSerializable(ProfileFeedFragment.BOOKMARKS, false)
putSerializable(ProfileFeedFragment.COLLECTIONS, true)
}
val returnArray: Array<UncachedFeedFragment<FeedContent>> = arrayOf(
profileGridFragment,
profileFeedFragment,
profileCollectionsFragment
)
// If we are viewing our own account, show bookmarks
if(account == null || account.id == user?.user_id) {
val profileBookmarksFragment = ProfileFeedFragment()
profileBookmarksFragment.arguments = Bundle().apply {
putSerializable(Account.ACCOUNT_TAG, account)
putSerializable(ProfileFeedFragment.PROFILE_GRID, true)
putSerializable(ProfileFeedFragment.BOOKMARKS, true)
}
return returnArray + profileBookmarksFragment
}
return returnArray
}
private fun setupTabs(
tabs: Array<UncachedFeedFragment<FeedContent>>
){
binding.viewPager.adapter = object : FragmentStateAdapter(this) {
override fun createFragment(position: Int): Fragment {
return tabs[position]
}
override fun getItemCount(): Int {
return tabs.size
}
}
TabLayoutMediator(binding.profileTabs, binding.viewPager) { tab, position ->
tab.tabLabelVisibility = TabLayout.TAB_LABEL_VISIBILITY_UNLABELED
when (position) {
0 -> {
tab.setText(R.string.grid_view)
tab.setIcon(R.drawable.grid_on_black_24dp)
}
1 -> {
tab.setText(R.string.feed_view)
tab.setIcon(R.drawable.feed_view)
}
2 -> {
tab.setText(R.string.collections)
tab.setIcon(R.drawable.collections)
}
3 -> {
tab.setText(R.string.bookmarks)
tab.setIcon(R.drawable.bookmark)
}
}
}.attach()
}
private fun setContent(account: Account?) {
if(account != null) {
setViews(account)
} else {
supportActionBar?.setTitle(R.string.menu_account)
lifecycleScope.launchWhenResumed {
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
val myAccount: Account = try {
api.verifyCredentials()
} catch (exception: Exception) {
Log.e("ProfileActivity:", exception.toString())
Toast.makeText(
applicationContext, "Could not get your profile",
Toast.LENGTH_SHORT
).show()
return@launchWhenResumed
}
updateUserInfoDb(db, myAccount)
setViews(myAccount)
}
}
if(account != null && account.id != user?.user_id) {
//if we aren't viewing our own account, activate follow button
activateFollow(account)
} else {
//if we *are* viewing our own account, activate the edit button
activateEditButton()
}
// On click open followers list
binding.nbFollowersTextView.setOnClickListener{ onClickFollowers(account) }
// On click open followers list
binding.nbFollowingTextView.setOnClickListener{ onClickFollowing(account) }
}
/**
* Populate profile page with user's data
*/
private fun setViews(account: Account) {
val profilePicture = binding.profilePictureImageView
setProfileImageFromURL(
View(applicationContext),
account.anyAvatar(),
profilePicture
)
binding.descriptionTextView.text = parseHTMLText(
account.note ?: "", emptyList(), apiHolder,
binding.descriptionTextView.context,
lifecycleScope
)
// This is so that the clicks in the text (eg #, @) work.
binding.descriptionTextView.movementMethod = LinkMovementMethod.getInstance();
val displayName = account.getDisplayName()
binding.accountNameTextView.text = displayName
supportActionBar?.title = displayName
if(displayName != "@${account.acct}") {
supportActionBar?.subtitle = "@${account.acct}"
}
binding.nbPostsTextView.text = resources.getQuantityString(
R.plurals.nb_posts,
account.statuses_count ?: 0,
account.statuses_count ?: 0
)
binding.nbFollowersTextView.text = resources.getQuantityString(
R.plurals.nb_followers,
account.followers_count ?: 0,
account.followers_count ?: 0
)
binding.nbFollowingTextView.text = resources.getQuantityString(
R.plurals.nb_following,
account.following_count ?: 0,
account.following_count ?: 0
)
}
private val editResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
// Profile was edited, reload
setContent(null)
}
}
private fun onClickEditButton() {
editResult.launch(Intent(this, EditProfileActivity::class.java))
}
private fun onClickFollowers(account: Account?) {
val intent = Intent(this, FollowsActivity::class.java)
intent.putExtra(Account.FOLLOWERS_TAG, true)
intent.putExtra(Account.ACCOUNT_TAG, account)
ContextCompat.startActivity(this, intent, null)
}
private fun onClickFollowing(account: Account?) {
val intent = Intent(this, FollowsActivity::class.java)
intent.putExtra(Account.FOLLOWERS_TAG, false)
intent.putExtra(Account.ACCOUNT_TAG, account)
ContextCompat.startActivity(this, intent, null)
}
private fun activateEditButton() {
// Edit button redirects to Pixelfed's "edit account" page
binding.editButton.apply {
visibility = View.VISIBLE
setOnClickListener{ onClickEditButton() }
}
}
/**
* Set up follow button
*/
private fun activateFollow(account: Account) {
// Get relationship between the two users (credential and this) and set followButton accordingly
lifecycleScope.launch {
try {
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
val relationship = api.checkRelationships(
listOf(account.id.orEmpty())
).firstOrNull()
if(relationship != null){
if (relationship.following == true || relationship.requested == true) {
setOnClickUnfollow(account, relationship.requested == true)
} else {
setOnClickFollow(account)
}
binding.followButton.visibility = View.VISIBLE
}
} catch (exception: IOException) {
Log.e("FOLLOW ERROR", exception.toString())
Toast.makeText(
applicationContext, getString(R.string.follow_status_failed),
Toast.LENGTH_SHORT
).show()
} catch (exception: HttpException) {
Toast.makeText(
applicationContext, getString(R.string.follow_button_failed),
Toast.LENGTH_SHORT
).show()
}
}
}
private fun setOnClickFollow(account: Account) {
binding.followButton.apply {
setText(R.string.follow)
setOnClickListener {
lifecycleScope.launchWhenResumed {
try {
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
val rel = api.follow(account.id.orEmpty())
if(rel.following == true) setOnClickUnfollow(account, rel.requested == true)
else setOnClickFollow(account)
} catch (exception: Exception) {
Log.e("FOLLOW ERROR", exception.toString())
Toast.makeText(
applicationContext, getString(R.string.follow_error),
Toast.LENGTH_SHORT
).show()
}
}
}
}
}
private fun setOnClickUnfollow(account: Account, requested: Boolean) {
binding.followButton.apply {
if(account.locked == true && requested) {
setText(R.string.follow_requested)
} else setText(R.string.unfollow)
fun unfollow() {
lifecycleScope.launchWhenResumed {
try {
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
val rel = api.unfollow(account.id.orEmpty())
if(rel.following == false && rel.requested == false) setOnClickFollow(account)
else setOnClickUnfollow(account, rel.requested == true)
} catch (exception: IOException) {
Log.e("FOLLOW ERROR", exception.toString())
Toast.makeText(
applicationContext, getString(R.string.unfollow_error),
Toast.LENGTH_SHORT
).show()
} catch (exception: HttpException) {
Toast.makeText(
applicationContext, getString(R.string.unfollow_error),
Toast.LENGTH_SHORT
).show()
}
}
}
setOnClickListener {
if(account.locked == true && requested){
MaterialAlertDialogBuilder(context)
.setMessage(R.string.dialog_message_cancel_follow_request)
.setPositiveButton(android.R.string.ok) { _, _ ->
unfollow()
}
.setNegativeButton(android.R.string.cancel){_, _ -> }
.show()
} else unfollow()
}
}
}
}