Merge branch 'master' into click_notifications
This commit is contained in:
commit
4ef9f1cfc6
@ -2,25 +2,38 @@ package com.h.pixeldroid.posts.feeds
|
|||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.size
|
import androidx.core.view.size
|
||||||
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.paging.LoadStateAdapter
|
import androidx.paging.LoadStateAdapter
|
||||||
import androidx.paging.PagingDataAdapter
|
import androidx.paging.PagingDataAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.h.pixeldroid.R
|
import com.h.pixeldroid.R
|
||||||
import com.h.pixeldroid.databinding.FragmentFeedBinding
|
import com.h.pixeldroid.databinding.ErrorLayoutBinding
|
||||||
import com.h.pixeldroid.databinding.LoadStateFooterViewItemBinding
|
import com.h.pixeldroid.databinding.LoadStateFooterViewItemBinding
|
||||||
|
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
||||||
|
import com.h.pixeldroid.utils.api.objects.FeedContent
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows or hides the error in the different FeedFragments
|
* Shows or hides the error in the different FeedFragments
|
||||||
*/
|
*/
|
||||||
private fun showError(errorText: String, show: Boolean = true, binding: FragmentFeedBinding){
|
private fun showError(
|
||||||
|
errorText: String, show: Boolean = true,
|
||||||
|
motionLayout: MotionLayout,
|
||||||
|
errorLayout: ErrorLayoutBinding){
|
||||||
|
|
||||||
if(show) {
|
if(show) {
|
||||||
binding.motionLayout.transitionToEnd()
|
motionLayout.transitionToEnd()
|
||||||
binding.errorLayout.errorText.text = errorText
|
errorLayout.errorText.text = errorText
|
||||||
} else if(binding.motionLayout.progress == 1F){
|
} else if(motionLayout.progress == 1F) {
|
||||||
binding.motionLayout.transitionToStart()
|
motionLayout.transitionToStart()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,28 +42,33 @@ private fun showError(errorText: String, show: Boolean = true, binding: Fragment
|
|||||||
*
|
*
|
||||||
* Makes the UI respond to various [LoadState]s, including errors when an error message is shown.
|
* Makes the UI respond to various [LoadState]s, including errors when an error message is shown.
|
||||||
*/
|
*/
|
||||||
internal fun <T: Any> initAdapter(binding: FragmentFeedBinding, adapter: PagingDataAdapter<T, RecyclerView.ViewHolder>) {
|
internal fun <T: Any> initAdapter(
|
||||||
binding.list.adapter = adapter.withLoadStateFooter(
|
progressBar: ProgressBar, swipeRefreshLayout: SwipeRefreshLayout,
|
||||||
|
recyclerView: RecyclerView, motionLayout: MotionLayout, errorLayout: ErrorLayoutBinding,
|
||||||
|
adapter: PagingDataAdapter<T, RecyclerView.ViewHolder>) {
|
||||||
|
|
||||||
|
recyclerView.adapter = adapter.withLoadStateFooter(
|
||||||
footer = ReposLoadStateAdapter { adapter.retry() }
|
footer = ReposLoadStateAdapter { adapter.retry() }
|
||||||
)
|
)
|
||||||
|
|
||||||
adapter.addLoadStateListener { loadState ->
|
adapter.addLoadStateListener { loadState ->
|
||||||
|
|
||||||
if(!binding.progressBar.isVisible && binding.swipeRefreshLayout.isRefreshing) {
|
if(!progressBar.isVisible && swipeRefreshLayout.isRefreshing) {
|
||||||
// Stop loading spinner when loading is done
|
// Stop loading spinner when loading is done
|
||||||
binding.swipeRefreshLayout.isRefreshing = loadState.refresh is LoadState.Loading
|
swipeRefreshLayout.isRefreshing = loadState.refresh is LoadState.Loading
|
||||||
} else {
|
} else {
|
||||||
// ProgressBar should stop showing as soon as the source stops loading ("source"
|
// ProgressBar should stop showing as soon as the source stops loading ("source"
|
||||||
// meaning the database, so don't wait on the network)
|
// meaning the database, so don't wait on the network)
|
||||||
val sourceLoading = loadState.source.refresh is LoadState.Loading
|
val sourceLoading = loadState.source.refresh is LoadState.Loading
|
||||||
if(!sourceLoading && binding.list.size > 0){
|
if(!sourceLoading && recyclerView.size > 0){
|
||||||
binding.list.isVisible = true
|
recyclerView.isVisible = true
|
||||||
binding.progressBar.isVisible = false
|
progressBar.isVisible = false
|
||||||
} else if(binding.list.size == 0
|
} else if(recyclerView.size == 0
|
||||||
&& loadState.append is LoadState.NotLoading
|
&& loadState.append is LoadState.NotLoading
|
||||||
&& loadState.append.endOfPaginationReached){
|
&& loadState.append.endOfPaginationReached){
|
||||||
binding.progressBar.isVisible = false
|
progressBar.isVisible = false
|
||||||
showError(binding = binding, errorText = "Nothing to see here :(")
|
showError(motionLayout = motionLayout, errorLayout = errorLayout,
|
||||||
|
errorText = errorLayout.root.context.getString(R.string.empty_feed))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,12 +81,28 @@ internal fun <T: Any> initAdapter(binding: FragmentFeedBinding, adapter: PagingD
|
|||||||
?: loadState.prepend as? LoadState.Error
|
?: loadState.prepend as? LoadState.Error
|
||||||
?: loadState.refresh as? LoadState.Error
|
?: loadState.refresh as? LoadState.Error
|
||||||
errorState?.let {
|
errorState?.let {
|
||||||
showError(binding = binding, errorText = it.error.toString())
|
showError(motionLayout = motionLayout, errorLayout = errorLayout, errorText = it.error.toString())
|
||||||
|
}
|
||||||
|
if(errorState == null) {
|
||||||
|
showError(motionLayout = motionLayout, errorLayout = errorLayout, show = false, errorText = "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (errorState == null) showError(binding = binding, show = false, errorText = "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun launch(
|
||||||
|
job: Job?, lifecycleScope: LifecycleCoroutineScope, viewModel: FeedViewModel<FeedContent>,
|
||||||
|
pagingDataAdapter: PagingDataAdapter<FeedContent, RecyclerView.ViewHolder>): Job {
|
||||||
|
// Make sure we cancel the previous job before creating a new one
|
||||||
|
job?.cancel()
|
||||||
|
return lifecycleScope.launch {
|
||||||
|
viewModel.flow().collectLatest {
|
||||||
|
pagingDataAdapter.submitData(it)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter to the show the a [RecyclerView] item for a [LoadState], with a callback to retry if
|
* Adapter to the show the a [RecyclerView] item for a [LoadState], with a callback to retry if
|
||||||
|
@ -70,7 +70,8 @@ open class CachedFeedFragment<T: FeedContentDatabase> : BaseFragment() {
|
|||||||
|
|
||||||
binding = FragmentFeedBinding.inflate(layoutInflater)
|
binding = FragmentFeedBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
initAdapter(binding, adapter)
|
initAdapter(binding.progressBar, binding.swipeRefreshLayout,
|
||||||
|
binding.list, binding.motionLayout, binding.errorLayout, adapter)
|
||||||
|
|
||||||
//binding.progressBar.visibility = View.GONE
|
//binding.progressBar.visibility = View.GONE
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||||
|
@ -9,9 +9,9 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.paging.*
|
import androidx.paging.*
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.h.pixeldroid.posts.feeds.launch
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -30,20 +30,17 @@ open class UncachedFeedFragment<T: FeedContent> : BaseFragment() {
|
|||||||
internal lateinit var viewModel: FeedViewModel<T>
|
internal lateinit var viewModel: FeedViewModel<T>
|
||||||
internal lateinit var adapter: PagingDataAdapter<T, RecyclerView.ViewHolder>
|
internal lateinit var adapter: PagingDataAdapter<T, RecyclerView.ViewHolder>
|
||||||
|
|
||||||
private lateinit var binding: FragmentFeedBinding
|
lateinit var binding: FragmentFeedBinding
|
||||||
|
|
||||||
|
|
||||||
private var job: Job? = null
|
private var job: Job? = null
|
||||||
|
|
||||||
|
|
||||||
internal fun launch() {
|
internal fun launch() {
|
||||||
// Make sure we cancel the previous job before creating a new one
|
@Suppress("UNCHECKED_CAST")
|
||||||
job?.cancel()
|
job = launch(job, lifecycleScope,
|
||||||
job = lifecycleScope.launch {
|
viewModel as FeedViewModel<FeedContent>,
|
||||||
viewModel.flow().collectLatest {
|
adapter as PagingDataAdapter<FeedContent, RecyclerView.ViewHolder>)
|
||||||
adapter.submitData(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun initSearch() {
|
internal fun initSearch() {
|
||||||
@ -67,7 +64,8 @@ open class UncachedFeedFragment<T: FeedContent> : BaseFragment() {
|
|||||||
|
|
||||||
binding = FragmentFeedBinding.inflate(layoutInflater)
|
binding = FragmentFeedBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
initAdapter(binding, adapter)
|
initAdapter(binding.progressBar, binding.swipeRefreshLayout, binding.list,
|
||||||
|
binding.motionLayout, binding.errorLayout, adapter)
|
||||||
|
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||||
//It shouldn't be necessary to also retry() in addition to refresh(),
|
//It shouldn't be necessary to also retry() in addition to refresh(),
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.h.pixeldroid.posts.feeds.uncachedFeeds.profile
|
||||||
|
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||||
|
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||||
|
import com.h.pixeldroid.utils.api.objects.Status
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class ProfileContentRepository @ExperimentalPagingApi
|
||||||
|
@Inject constructor(
|
||||||
|
private val api: PixelfedAPI,
|
||||||
|
private val accessToken: String,
|
||||||
|
private val accountId: String
|
||||||
|
) : UncachedContentRepository<Status> {
|
||||||
|
override fun getStream(): Flow<PagingData<Status>> {
|
||||||
|
return Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
initialLoadSize = NETWORK_PAGE_SIZE,
|
||||||
|
pageSize = NETWORK_PAGE_SIZE,
|
||||||
|
enablePlaceholders = false),
|
||||||
|
pagingSourceFactory = {
|
||||||
|
ProfilePagingSource(api, accessToken, accountId)
|
||||||
|
}
|
||||||
|
).flow
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val NETWORK_PAGE_SIZE = 20
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.h.pixeldroid.posts.feeds.uncachedFeeds.profile
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||||
|
import com.h.pixeldroid.utils.api.objects.Status
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class ProfilePagingSource(
|
||||||
|
private val api: PixelfedAPI,
|
||||||
|
private val accessToken: String,
|
||||||
|
private val accountId: String
|
||||||
|
) : PagingSource<String, Status>() {
|
||||||
|
override suspend fun load(params: LoadParams<String>): LoadResult<String, Status> {
|
||||||
|
val position = params.key
|
||||||
|
return try {
|
||||||
|
val posts = api.accountPosts("Bearer $accessToken",
|
||||||
|
account_id = accountId,
|
||||||
|
max_id = position,
|
||||||
|
limit = params.loadSize
|
||||||
|
)
|
||||||
|
|
||||||
|
LoadResult.Page(
|
||||||
|
data = posts,
|
||||||
|
prevKey = null,
|
||||||
|
nextKey = posts.lastOrNull()?.id
|
||||||
|
)
|
||||||
|
} catch (exception: HttpException) {
|
||||||
|
LoadResult.Error(exception)
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
LoadResult.Error(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRefreshKey(state: PagingState<String, Status>): String? = null
|
||||||
|
}
|
@ -45,8 +45,7 @@ class SearchAccountFragment : UncachedFeedFragment<Account>() {
|
|||||||
query
|
query
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
).get(FeedViewModel::class.java) as FeedViewModel<Account>
|
||||||
.get(FeedViewModel::class.java) as FeedViewModel<Account>
|
|
||||||
|
|
||||||
launch()
|
launch()
|
||||||
initSearch()
|
initSearch()
|
||||||
|
@ -1,44 +1,61 @@
|
|||||||
package com.h.pixeldroid.profile
|
package com.h.pixeldroid.profile
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.annotation.StringRes
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.PagingDataAdapter
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.h.pixeldroid.R
|
import com.h.pixeldroid.R
|
||||||
import com.h.pixeldroid.databinding.ActivityProfileBinding
|
import com.h.pixeldroid.databinding.ActivityProfileBinding
|
||||||
|
import com.h.pixeldroid.databinding.FragmentProfilePostsBinding
|
||||||
|
import com.h.pixeldroid.posts.PostActivity
|
||||||
|
import com.h.pixeldroid.posts.feeds.initAdapter
|
||||||
|
import com.h.pixeldroid.posts.feeds.launch
|
||||||
|
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
||||||
|
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||||
|
import com.h.pixeldroid.posts.feeds.uncachedFeeds.profile.ProfileContentRepository
|
||||||
import com.h.pixeldroid.posts.parseHTMLText
|
import com.h.pixeldroid.posts.parseHTMLText
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import com.h.pixeldroid.utils.BaseActivity
|
||||||
import com.h.pixeldroid.utils.ImageConverter
|
import com.h.pixeldroid.utils.ImageConverter
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import com.h.pixeldroid.utils.api.objects.Account
|
||||||
|
import com.h.pixeldroid.utils.api.objects.FeedContent
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import com.h.pixeldroid.utils.api.objects.Status
|
||||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
||||||
import com.h.pixeldroid.utils.openUrl
|
import com.h.pixeldroid.utils.openUrl
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Callback
|
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import retrofit2.Response
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class ProfileActivity : BaseActivity() {
|
class ProfileActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var pixelfedAPI : PixelfedAPI
|
private lateinit var pixelfedAPI : PixelfedAPI
|
||||||
private lateinit var adapter : ProfilePostsRecyclerViewAdapter
|
|
||||||
private lateinit var accessToken : String
|
private lateinit var accessToken : String
|
||||||
private lateinit var domain : String
|
private lateinit var domain : String
|
||||||
private var user: UserDatabaseEntity? = null
|
private lateinit var accountId : String
|
||||||
|
|
||||||
private lateinit var binding: ActivityProfileBinding
|
private lateinit var binding: ActivityProfileBinding
|
||||||
|
private lateinit var profileAdapter: PagingDataAdapter<Status, RecyclerView.ViewHolder>
|
||||||
|
private lateinit var viewModel: FeedViewModel<Status>
|
||||||
|
|
||||||
|
private var user: UserDatabaseEntity? = null
|
||||||
|
private var job: Job? = null
|
||||||
|
|
||||||
|
@ExperimentalPagingApi
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityProfileBinding.inflate(layoutInflater)
|
binding = ActivityProfileBinding.inflate(layoutInflater)
|
||||||
@ -52,19 +69,49 @@ class ProfileActivity : BaseActivity() {
|
|||||||
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
||||||
accessToken = user?.accessToken.orEmpty()
|
accessToken = user?.accessToken.orEmpty()
|
||||||
|
|
||||||
// Set posts RecyclerView as a grid with 3 columns
|
|
||||||
binding.profilePostsRecyclerView.layoutManager = GridLayoutManager(applicationContext, 3)
|
|
||||||
adapter = ProfilePostsRecyclerViewAdapter()
|
|
||||||
binding.profilePostsRecyclerView.adapter = adapter
|
|
||||||
|
|
||||||
// Set profile according to given account
|
// Set profile according to given account
|
||||||
val account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
|
val account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
|
||||||
|
accountId = account?.id ?: user!!.user_id
|
||||||
|
|
||||||
setContent(account)
|
// get the view model
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
viewModel = ViewModelProvider(this, ProfileViewModelFactory(
|
||||||
|
ProfileContentRepository(
|
||||||
|
apiHolder.setDomainToCurrentUser(db),
|
||||||
|
db.userDao().getActiveUser()!!.accessToken,
|
||||||
|
accountId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).get(FeedViewModel::class.java) as FeedViewModel<Status>
|
||||||
|
|
||||||
|
profileAdapter = ProfilePostsAdapter()
|
||||||
|
initAdapter(binding.profileProgressBar, binding.profileRefreshLayout,
|
||||||
|
binding.profilePostsRecyclerView, binding.motionLayout, binding.profileErrorLayout,
|
||||||
|
profileAdapter)
|
||||||
|
|
||||||
|
binding.profilePostsRecyclerView.layoutManager = GridLayoutManager(this, 3)
|
||||||
|
|
||||||
binding.profileRefreshLayout.setOnRefreshListener {
|
binding.profileRefreshLayout.setOnRefreshListener {
|
||||||
getAndSetAccount(account?.id ?: user!!.user_id)
|
profileAdapter.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setContent(account)
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
job = launch(job, lifecycleScope, viewModel as FeedViewModel<FeedContent>,
|
||||||
|
profileAdapter as PagingDataAdapter<FeedContent, RecyclerView.ViewHolder>)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows or hides the error in the different FeedFragments
|
||||||
|
*/
|
||||||
|
private fun showError(errorText: String = "Something went wrong while loading", show: Boolean = true){
|
||||||
|
if(show){
|
||||||
|
binding.profileProgressBar.visibility = View.GONE
|
||||||
|
binding.motionLayout.transitionToEnd()
|
||||||
|
} else if(binding.motionLayout.progress == 1F) {
|
||||||
|
binding.motionLayout.transitionToStart()
|
||||||
|
}
|
||||||
|
binding.profileRefreshLayout.isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
@ -75,7 +122,6 @@ class ProfileActivity : BaseActivity() {
|
|||||||
private fun setContent(account: Account?) {
|
private fun setContent(account: Account?) {
|
||||||
if(account != null) {
|
if(account != null) {
|
||||||
setViews(account)
|
setViews(account)
|
||||||
setPosts(account)
|
|
||||||
} else {
|
} else {
|
||||||
lifecycleScope.launchWhenResumed {
|
lifecycleScope.launchWhenResumed {
|
||||||
val myAccount: Account = try {
|
val myAccount: Account = try {
|
||||||
@ -87,16 +133,16 @@ class ProfileActivity : BaseActivity() {
|
|||||||
return@launchWhenResumed showError()
|
return@launchWhenResumed showError()
|
||||||
}
|
}
|
||||||
setViews(myAccount)
|
setViews(myAccount)
|
||||||
// Populate profile page with user's posts
|
|
||||||
setPosts(myAccount)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(account != null && account.id != user?.user_id) {
|
||||||
//if we aren't viewing our own account, activate follow button
|
//if we aren't viewing our own account, activate follow button
|
||||||
if(account != null && account.id != user?.user_id) activateFollow(account)
|
activateFollow(account)
|
||||||
|
} else {
|
||||||
//if we *are* viewing our own account, activate the edit button
|
//if we *are* viewing our own account, activate the edit button
|
||||||
else activateEditButton()
|
activateEditButton()
|
||||||
|
}
|
||||||
|
|
||||||
// On click open followers list
|
// On click open followers list
|
||||||
binding.nbFollowersTextView.setOnClickListener{ onClickFollowers(account) }
|
binding.nbFollowersTextView.setOnClickListener{ onClickFollowers(account) }
|
||||||
@ -104,31 +150,6 @@ class ProfileActivity : BaseActivity() {
|
|||||||
binding.nbFollowingTextView.setOnClickListener{ onClickFollowing(account) }
|
binding.nbFollowingTextView.setOnClickListener{ onClickFollowing(account) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAndSetAccount(id: String){
|
|
||||||
lifecycleScope.launchWhenCreated {
|
|
||||||
val account = try{
|
|
||||||
pixelfedAPI.getAccount("Bearer $accessToken", id)
|
|
||||||
} catch (exception: IOException) {
|
|
||||||
Log.e("ProfileActivity:", exception.toString())
|
|
||||||
return@launchWhenCreated showError()
|
|
||||||
} catch (exception: HttpException) {
|
|
||||||
return@launchWhenCreated showError()
|
|
||||||
}
|
|
||||||
setContent(account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showError(@StringRes errorText: Int = R.string.loading_toast, show: Boolean = true){
|
|
||||||
val motionLayout = binding.motionLayout
|
|
||||||
if(show){
|
|
||||||
motionLayout.transitionToEnd()
|
|
||||||
} else {
|
|
||||||
motionLayout.transitionToStart()
|
|
||||||
}
|
|
||||||
binding.profileProgressBar.visibility = View.GONE
|
|
||||||
binding.profileRefreshLayout.isRefreshing = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate profile page with user's data
|
* Populate profile page with user's data
|
||||||
*/
|
*/
|
||||||
@ -174,37 +195,13 @@ class ProfileActivity : BaseActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Populate profile page with user's posts
|
|
||||||
*/
|
|
||||||
private fun setPosts(account: Account) {
|
|
||||||
pixelfedAPI.accountPosts("Bearer $accessToken", account_id = account.id)
|
|
||||||
.enqueue(object : Callback<List<Status>> {
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
|
|
||||||
showError()
|
|
||||||
Log.e("ProfileActivity.Posts:", t.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResponse(
|
|
||||||
call: Call<List<Status>>,
|
|
||||||
response: Response<List<Status>>
|
|
||||||
) {
|
|
||||||
if (response.code() == 200) {
|
|
||||||
val statuses = response.body()!!
|
|
||||||
adapter.addPosts(statuses)
|
|
||||||
showError(show = false)
|
|
||||||
} else {
|
|
||||||
showError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onClickEditButton() {
|
private fun onClickEditButton() {
|
||||||
val url = "$domain/settings/home"
|
val url = "$domain/settings/home"
|
||||||
|
|
||||||
if (!openUrl(url)) Log.e("ProfileActivity", "Cannot open this link")
|
if(!openUrl(url)) {
|
||||||
|
Snackbar.make(binding.root, getString(R.string.edit_link_failed),
|
||||||
|
Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onClickFollowers(account: Account?) {
|
private fun onClickFollowers(account: Account?) {
|
||||||
@ -316,3 +313,88 @@ class ProfileActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileViewModelFactory @ExperimentalPagingApi constructor(
|
||||||
|
private val searchContentRepository: UncachedContentRepository<Status>
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
|
@ExperimentalPagingApi
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
if (modelClass.isAssignableFrom(FeedViewModel::class.java)) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return FeedViewModel(searchContentRepository) as T
|
||||||
|
}
|
||||||
|
throw IllegalArgumentException("Unknown ViewModel class")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ProfilePostsViewHolder(binding: FragmentProfilePostsBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
private val postPreview: ImageView = binding.postPreview
|
||||||
|
private val albumIcon: ImageView = binding.albumIcon
|
||||||
|
|
||||||
|
fun bind(post: Status) {
|
||||||
|
|
||||||
|
if(post.sensitive!!) {
|
||||||
|
ImageConverter.setSquareImageFromDrawable(
|
||||||
|
itemView,
|
||||||
|
AppCompatResources.getDrawable(itemView.context, R.drawable.ic_sensitive),
|
||||||
|
postPreview
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ImageConverter.setSquareImageFromURL(itemView, post.getPostPreviewURL(), postPreview)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(post.media_attachments?.size ?: 0 > 1) {
|
||||||
|
albumIcon.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
albumIcon.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
postPreview.setOnClickListener {
|
||||||
|
val intent = Intent(postPreview.context, PostActivity::class.java)
|
||||||
|
intent.putExtra(Status.POST_TAG, post)
|
||||||
|
postPreview.context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun create(parent: ViewGroup): ProfilePostsViewHolder {
|
||||||
|
val itemBinding = FragmentProfilePostsBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context), parent, false
|
||||||
|
)
|
||||||
|
return ProfilePostsViewHolder(itemBinding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ProfilePostsAdapter : PagingDataAdapter<Status, RecyclerView.ViewHolder>(
|
||||||
|
UIMODEL_COMPARATOR
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return ProfilePostsViewHolder.create(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
val post = getItem(position)
|
||||||
|
|
||||||
|
post?.let {
|
||||||
|
(holder as ProfilePostsViewHolder).bind(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val UIMODEL_COMPARATOR = object : DiffUtil.ItemCallback<Status>() {
|
||||||
|
override fun areItemsTheSame(oldItem: Status, newItem: Status): Boolean {
|
||||||
|
return oldItem.id == newItem.id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Status, newItem: Status): Boolean =
|
||||||
|
oldItem.content == newItem.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -34,10 +34,15 @@ class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostViewHolde
|
|||||||
override fun onBindViewHolder(holder: ProfilePostViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ProfilePostViewHolder, position: Int) {
|
||||||
val post = posts[position]
|
val post = posts[position]
|
||||||
|
|
||||||
if (post.sensitive!!)
|
if(post.sensitive!!) {
|
||||||
setSquareImageFromDrawable(holder.postView, getDrawable(holder.postView.context, R.drawable.ic_sensitive), holder.postPreview)
|
setSquareImageFromDrawable(
|
||||||
else
|
holder.postView,
|
||||||
|
getDrawable(holder.postView.context, R.drawable.ic_sensitive),
|
||||||
|
holder.postPreview
|
||||||
|
)
|
||||||
|
} else {
|
||||||
setSquareImageFromURL(holder.postView, post.getPostPreviewURL(), holder.postPreview)
|
setSquareImageFromURL(holder.postView, post.getPostPreviewURL(), holder.postPreview)
|
||||||
|
}
|
||||||
|
|
||||||
if(post.media_attachments?.size ?: 0 > 1) {
|
if(post.media_attachments?.size ?: 0 > 1) {
|
||||||
holder.albumIcon.visibility = View.VISIBLE
|
holder.albumIcon.visibility = View.VISIBLE
|
||||||
@ -55,6 +60,7 @@ class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostViewHolde
|
|||||||
override fun getItemCount(): Int = posts.size
|
override fun getItemCount(): Int = posts.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ProfilePostViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
|
class ProfilePostViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
|
||||||
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
|
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
|
||||||
val albumIcon: ImageView = postView.findViewById(R.id.albumIcon)
|
val albumIcon: ImageView = postView.findViewById(R.id.albumIcon)
|
||||||
|
@ -2,10 +2,7 @@ package com.h.pixeldroid.utils.api
|
|||||||
|
|
||||||
import com.h.pixeldroid.utils.api.objects.*
|
import com.h.pixeldroid.utils.api.objects.*
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Single
|
|
||||||
import kotlinx.coroutines.Deferred
|
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
|
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
|
||||||
@ -208,10 +205,13 @@ interface PixelfedAPI {
|
|||||||
|
|
||||||
|
|
||||||
@GET("/api/v1/accounts/{id}/statuses")
|
@GET("/api/v1/accounts/{id}/statuses")
|
||||||
fun accountPosts(
|
suspend fun accountPosts(
|
||||||
@Header("Authorization") authorization: String,
|
@Header("Authorization") authorization: String,
|
||||||
@Path("id") account_id: String? = null
|
@Path("id") account_id: String,
|
||||||
): Call<List<Status>>
|
@Query("min_id") min_id: String? = null,
|
||||||
|
@Query("max_id") max_id: String?,
|
||||||
|
@Query("limit") limit: Int
|
||||||
|
) : List<Status>
|
||||||
|
|
||||||
@GET("/api/v1/accounts/relationships")
|
@GET("/api/v1/accounts/relationships")
|
||||||
suspend fun checkRelationships(
|
suspend fun checkRelationships(
|
||||||
|
@ -18,27 +18,26 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/profilePictureImageView"
|
android:id="@+id/profilePictureImageView"
|
||||||
android:layout_width="120dp"
|
android:layout_width="88dp"
|
||||||
android:layout_height="120dp"
|
android:layout_height="88dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="20dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="6dp"
|
||||||
|
android:contentDescription="@string/profile_picture"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:srcCompat="@tools:sample/avatars"
|
tools:srcCompat="@tools:sample/avatars" />
|
||||||
android:contentDescription="@string/profile_picture" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/nbPostsTextView"
|
android:id="@+id/nbPostsTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/default_nposts"
|
android:text="@string/default_nposts"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/profilePictureImageView"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintHorizontal_chainStyle="spread"
|
app:layout_constraintTop_toBottomOf="@+id/descriptionTextView" />
|
||||||
app:layout_constraintStart_toEndOf="@+id/profilePictureImageView"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/profilePictureImageView" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/nbFollowersTextView"
|
android:id="@+id/nbFollowersTextView"
|
||||||
@ -47,6 +46,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/default_nfollowers"
|
android:text="@string/default_nfollowers"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/nbPostsTextView"
|
app:layout_constraintBottom_toBottomOf="@+id/nbPostsTextView"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/nbFollowingTextView"
|
app:layout_constraintEnd_toStartOf="@+id/nbFollowingTextView"
|
||||||
app:layout_constraintStart_toEndOf="@+id/nbPostsTextView"
|
app:layout_constraintStart_toEndOf="@+id/nbPostsTextView"
|
||||||
@ -56,10 +56,11 @@
|
|||||||
android:id="@+id/nbFollowingTextView"
|
android:id="@+id/nbFollowingTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="20dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/default_nfollowing"
|
android:text="@string/default_nfollowing"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/nbFollowersTextView"
|
app:layout_constraintBottom_toBottomOf="@+id/nbFollowersTextView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/nbFollowersTextView" />
|
app:layout_constraintTop_toTopOf="@+id/nbFollowersTextView" />
|
||||||
@ -85,7 +86,6 @@
|
|||||||
android:layout_marginLeft="20dp"
|
android:layout_marginLeft="20dp"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
android:layout_marginRight="20dp"
|
android:layout_marginRight="20dp"
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountNameTextView" />
|
app:layout_constraintTop_toBottomOf="@id/accountNameTextView" />
|
||||||
@ -98,27 +98,26 @@
|
|||||||
android:text="@string/follow"
|
android:text="@string/follow"
|
||||||
android:textColor="@color/colorButtonText"
|
android:textColor="@color/colorButtonText"
|
||||||
android:visibility="invisible"
|
android:visibility="invisible"
|
||||||
tools:layout_editor_absoluteX="16dp"
|
app:layout_constraintBottom_toBottomOf="@+id/profilePictureImageView"
|
||||||
tools:layout_editor_absoluteY="185dp"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
tools:visibility="visible"
|
android:layout_marginEnd="20dp"
|
||||||
app:layout_constraintStart_toStartOf="@+id/profilePictureImageView"
|
app:layout_constraintTop_toTopOf="@+id/profilePictureImageView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/descriptionTextView"/>
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/editButton"
|
android:id="@+id/editButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:backgroundTint="@color/colorButtonBg"
|
android:backgroundTint="@color/colorButtonBg"
|
||||||
android:text="@string/edit_profile"
|
android:text="@string/edit_profile"
|
||||||
android:textColor="@color/colorButtonText"
|
android:textColor="@color/colorButtonText"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:icon="@drawable/ic_baseline_open_in_browser_24"
|
app:icon="@drawable/ic_baseline_open_in_browser_24"
|
||||||
app:iconTint="@color/colorButtonText"
|
app:iconTint="@color/colorButtonText"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintBottom_toBottomOf="@+id/profilePictureImageView"
|
||||||
app:layout_constraintStart_toStartOf="@+id/profilePictureImageView"
|
app:layout_constraintStart_toEndOf="@+id/profilePictureImageView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/descriptionTextView" />
|
app:layout_constraintTop_toTopOf="@+id/profilePictureImageView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
@ -130,21 +129,23 @@
|
|||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/followButton" />
|
app:layout_constraintTop_toBottomOf="@id/nbFollowersTextView" />
|
||||||
|
|
||||||
|
|
||||||
<androidx.constraintlayout.motion.widget.MotionLayout
|
<androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
android:id="@+id/motionLayout"
|
android:id="@+id/motionLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="18dp"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
app:layoutDescription="@xml/error_layout_xml_error_scene"
|
app:layoutDescription="@xml/error_layout_xml_error_scene"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/followButton"
|
app:layout_constraintTop_toBottomOf="@id/nbFollowersTextView"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<include
|
<include
|
||||||
|
android:id="@+id/profileErrorLayout"
|
||||||
layout="@layout/error_layout"
|
layout="@layout/error_layout"
|
||||||
tools:layout_editor_absoluteX="50dp" />
|
tools:layout_editor_absoluteX="50dp" />
|
||||||
|
|
||||||
@ -160,6 +161,7 @@
|
|||||||
|
|
||||||
</androidx.constraintlayout.motion.widget.MotionLayout>
|
</androidx.constraintlayout.motion.widget.MotionLayout>
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
@ -127,24 +127,20 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
|
|||||||
<string name="post_is_album">This post is an album</string>
|
<string name="post_is_album">This post is an album</string>
|
||||||
<!-- Profile page -->
|
<!-- Profile page -->
|
||||||
<plurals name="nb_posts">
|
<plurals name="nb_posts">
|
||||||
<item quantity="one">"%d
|
<item quantity="one">"%d\nPost"</item>
|
||||||
Post"</item>
|
<item quantity="other">"%d\nPosts"</item>
|
||||||
<item quantity="other">"%d
|
|
||||||
Posts"</item>
|
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="nb_followers">
|
<plurals name="nb_followers">
|
||||||
<item quantity="one">"%d
|
<item quantity="one">"%d\nFollower"</item>
|
||||||
Follower"</item>
|
<item quantity="other">"%d\nFollowers"</item>
|
||||||
<item quantity="other">"%d
|
|
||||||
Followers"</item>
|
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="nb_following">
|
<plurals name="nb_following">
|
||||||
<item quantity="one">"%d
|
<item quantity="one">"%d\nFollowing"</item>
|
||||||
Following"</item>
|
<item quantity="other">"%d\nFollowing"</item>
|
||||||
<item quantity="other">"%d
|
|
||||||
Following"</item>
|
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="follow_status_failed">Could not get follow status</string>
|
<string name="follow_status_failed">Could not get follow status</string>
|
||||||
|
<string name="edit_link_failed">Failed to open edit page</string>
|
||||||
|
<string name="empty_feed">Nothing to see here :(</string>
|
||||||
<string name="follow_button_failed">Could not display follow button</string>
|
<string name="follow_button_failed">Could not display follow button</string>
|
||||||
<string name="follow_error">Could not follow</string>
|
<string name="follow_error">Could not follow</string>
|
||||||
<string name="action_not_allowed">This action is not allowed</string>
|
<string name="action_not_allowed">This action is not allowed</string>
|
||||||
|
@ -15,7 +15,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
Loading…
x
Reference in New Issue
Block a user