Add album icon, fix some API calls, remove superfluous API call

This commit is contained in:
Matthieu 2020-10-31 22:11:01 +01:00
parent 2e88e49875
commit d0015b805c
12 changed files with 123 additions and 75 deletions

View File

@ -19,9 +19,6 @@ class FollowsActivity : AppCompatActivity() {
private var followsFragment = AccountListFragment()
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -35,26 +32,8 @@ class FollowsActivity : AppCompatActivity() {
val followers = intent.getSerializableExtra(FOLLOWERS_TAG) as Boolean
if(account == null) {
val user = db.userDao().getActiveUser()
val accessToken = user?.accessToken.orEmpty()
val pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
pixelfedAPI.verifyCredentials("Bearer $accessToken").enqueue(object :
Callback<Account> {
override fun onFailure(call: Call<Account>, t: Throwable) {
Log.e("Cannot get account id", t.toString())
}
override fun onResponse(call: Call<Account>, response: Response<Account>) {
if(response.code() == 200) {
val id = response.body()!!.id
val displayName = response.body()!!.display_name
startFragment(id, displayName, followers)
}
}
})
val user = db.userDao().getActiveUser()!!
startFragment(user.user_id, user.display_name, followers)
} else {
startFragment(account.id, account.display_name, followers)
}

View File

@ -17,6 +17,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.h.pixeldroid.adapters.ProfilePostsRecyclerViewAdapter
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Relationship
@ -35,6 +36,8 @@ class ProfileActivity : AppCompatActivity() {
private lateinit var accessToken : String
private lateinit var domain : String
private var account: Account? = null
private var user: UserDatabaseEntity? = null
@Inject
lateinit var db: AppDatabase
@ -49,7 +52,7 @@ class ProfileActivity : AppCompatActivity() {
(this.application as Pixeldroid).getAppComponent().inject(this)
val user = db.userDao().getActiveUser()
user = db.userDao().getActiveUser()
domain = user?.instance_uri.orEmpty()
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
@ -73,11 +76,10 @@ class ProfileActivity : AppCompatActivity() {
// Set profile according to given account
account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
account?.let {
if(account != null){
setViews()
activateFollow()
setPosts()
} ?: run {
} else {
pixelfedAPI.verifyCredentials("Bearer $accessToken")
.enqueue(object : Callback<Account> {
override fun onResponse(call: Call<Account>, response: Response<Account>) {
@ -94,13 +96,14 @@ class ProfileActivity : AppCompatActivity() {
Log.e("ProfileActivity:", t.toString())
}
})
// Edit button redirects to Pixelfed's "edit account" page
val editButton = findViewById<Button>(R.id.editButton)
editButton.visibility = View.VISIBLE
editButton.setOnClickListener{ onClickEditButton() }
}
//if we aren't viewing our own account, activate follow button
if(account != null && account!!.id != user?.user_id) activateFollow()
//if we *are* viewing our own account, activate the edit button
else activateEditButton()
// On click open followers list
findViewById<TextView>(R.id.nbFollowersTextView).setOnClickListener{ onClickFollowers() }
// On click open followers list
@ -187,7 +190,7 @@ class ProfileActivity : AppCompatActivity() {
private fun onClickFollowers() {
val intent = Intent(this, FollowsActivity::class.java)
intent.putExtra(Account.FOLLOWERS_TAG, true)
intent.putExtra(Account.ACCOUNT_ID_TAG, account?.id)
intent.putExtra(Account.ACCOUNT_TAG, account)
ContextCompat.startActivity(this, intent, null)
}
@ -200,6 +203,13 @@ class ProfileActivity : AppCompatActivity() {
ContextCompat.startActivity(this, intent, null)
}
private fun activateEditButton() {
// Edit button redirects to Pixelfed's "edit account" page
val editButton = findViewById<Button>(R.id.editButton)
editButton.visibility = View.VISIBLE
editButton.setOnClickListener{ onClickEditButton() }
}
/**
* Set up follow button
*/

View File

@ -16,7 +16,7 @@ import com.h.pixeldroid.utils.ImageConverter.Companion.setSquareImageFromURL
/**
* [RecyclerView.Adapter] that can display a list of [Status]s
*/
class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostsRecyclerViewAdapter.ViewHolder>() {
class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostViewHolder>() {
private val posts: ArrayList<Status> = ArrayList()
fun addPosts(newPosts : List<Status>) {
@ -25,13 +25,13 @@ class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostsRecycler
notifyItemRangeInserted(size, newPosts.size)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfilePostViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_profile_posts, parent, false)
return ViewHolder(view)
return ProfilePostViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
override fun onBindViewHolder(holder: ProfilePostViewHolder, position: Int) {
val post = posts[position]
if (post.sensitive!!)
@ -39,6 +39,10 @@ class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostsRecycler
else
setSquareImageFromURL(holder.postView, post.getPostPreviewURL(), holder.postPreview)
if(post.media_attachments?.size ?: 0 > 1){
holder.albumIcon.visibility = View.VISIBLE
}
holder.postPreview.setOnClickListener {
val intent = Intent(holder.postPreview.context, PostActivity::class.java)
intent.putExtra(Status.POST_TAG, post)
@ -47,8 +51,9 @@ class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostsRecycler
}
override fun getItemCount(): Int = posts.size
inner class ViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
}
}
class ProfilePostViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
val albumIcon: ImageView = postView.findViewById(R.id.albumIcon)
}

View File

@ -184,7 +184,7 @@ interface PixelfedAPI {
@Query("q") q: String,
@Query("resolve") resolve: Boolean? = null,
@Query("limit") limit: String? = null,
@Query("offset") offset: Int? = null,
@Query("offset") offset: String? = null,
@Query("following") following: Boolean? = null
): Call<Results>
@ -229,7 +229,8 @@ interface PixelfedAPI {
@Header("Authorization") authorization: String,
@Query("max_id") max_id: String? = null,
@Query("since_id") since_id: String? = null,
@Query("limit") limit: Number? = null
@Query("limit") limit: Number? = null,
@Query("page") page: String? = null
) : Call<List<Account>>
@GET("/api/v1/accounts/{id}/following")
@ -238,7 +239,8 @@ interface PixelfedAPI {
@Header("Authorization") authorization: String,
@Query("max_id") max_id: String? = null,
@Query("since_id") since_id: String? = null,
@Query("limit") limit: Number? = 40
@Query("limit") limit: Number? = 40,
@Query("page") page: String? = null
) : Call<List<Account>>
@GET("/api/v1/accounts/{id}")

View File

@ -11,6 +11,7 @@ import android.view.ViewGroup
import android.widget.*
import androidx.annotation.StringRes
import androidx.appcompat.widget.SearchView
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -18,6 +19,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.PostActivity
import com.h.pixeldroid.R
import com.h.pixeldroid.adapters.ProfilePostViewHolder
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.di.PixelfedAPIHolder
@ -29,7 +31,6 @@ import com.mikepenz.iconics.IconicsColor
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.color
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.paddingDp
import com.mikepenz.iconics.utils.sizeDp
import kotlinx.android.synthetic.main.fragment_search.*
@ -49,6 +50,7 @@ class SearchDiscoverFragment : Fragment() {
private lateinit var accessToken: String
private lateinit var discoverProgressBar: ProgressBar
private lateinit var discoverRefreshLayout: SwipeRefreshLayout
@Inject
lateinit var db: AppDatabase
@ -108,10 +110,11 @@ class SearchDiscoverFragment : Fragment() {
}
fun showError(@StringRes errorText: Int = R.string.loading_toast, show: Boolean = true){
val motionLayout = view?.findViewById<MotionLayout>(R.id.motionLayout)
if(show){
motionLayout.transitionToEnd()
motionLayout?.transitionToEnd()
} else {
motionLayout.transitionToStart()
motionLayout?.transitionToStart()
}
discoverRefreshLayout.isRefreshing = false
discoverProgressBar.visibility = View.GONE
@ -140,10 +143,11 @@ class SearchDiscoverFragment : Fragment() {
}
})
}
/**
* [RecyclerView.Adapter] that can display a list of [DiscoverPost]s
*/
class DiscoverRecyclerViewAdapter: RecyclerView.Adapter<DiscoverRecyclerViewAdapter.ViewHolder>() {
class DiscoverRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostViewHolder>() {
private val posts: ArrayList<DiscoverPost> = ArrayList()
fun addPosts(newPosts : List<DiscoverPost>) {
@ -152,14 +156,17 @@ class SearchDiscoverFragment : Fragment() {
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfilePostViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_profile_posts, parent, false)
return ViewHolder(view)
return ProfilePostViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
override fun onBindViewHolder(holder: ProfilePostViewHolder, position: Int) {
val post = posts[position]
if(post.type?.contains("album") == true) {
holder.albumIcon.visibility = View.VISIBLE
}
ImageConverter.setSquareImageFromURL(holder.postView, post.thumb, holder.postPreview)
holder.postPreview.setOnClickListener {
val intent = Intent(holder.postView.context, PostActivity::class.java)
@ -169,9 +176,5 @@ class SearchDiscoverFragment : Fragment() {
}
override fun getItemCount(): Int = posts.size
inner class ViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
}
}
}

View File

@ -9,7 +9,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.RecyclerView
import androidx.lifecycle.Observer
@ -34,7 +33,9 @@ open class AccountListFragment : FeedFragment() {
lateinit var profilePicRequest: RequestBuilder<Drawable>
protected lateinit var adapter : FeedsRecyclerViewAdapter<Account, AccountsRecyclerViewAdapter.ViewHolder>
lateinit var factory: FeedDataSourceFactory<String, Account>
lateinit var content: LiveData<PagedList<Account>>
private var currentPage = 1
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@ -62,7 +63,7 @@ open class AccountListFragment : FeedFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val content = makeContent()
content = makeContent()
content.observe(viewLifecycleOwner,
Observer { c ->
@ -74,6 +75,7 @@ open class AccountListFragment : FeedFragment() {
swipeRefreshLayout.setOnRefreshListener {
showError(show = false)
currentPage = 0
//by invalidating data, loadInitial will be called again
factory.liveData.value!!.invalidate()
}
@ -98,7 +100,7 @@ open class AccountListFragment : FeedFragment() {
//We use the id as the key
override fun getKey(item: Account): String {
return item.id
return currentPage.toString()
}
override fun makeInitialCall(requestedLoadSize: Int): Call<List<Account>> {
@ -116,15 +118,18 @@ open class AccountListFragment : FeedFragment() {
}
override fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Account>> {
// Pixelfed and Mastodon don't implement this in the same fashion. Pixelfed uses
// Laravel's paging mechanism, while Mastodon uses the Link header for pagination.
// No need to know which is which, they should ignore the non-relevant argument
return if (following) {
pixelfedAPI.followers(
id, "Bearer $accessToken",
since_id = key, limit = requestedLoadSize
limit = requestedLoadSize, page = key, max_id = key
)
} else {
pixelfedAPI.following(
id, "Bearer $accessToken",
since_id = key, limit = requestedLoadSize
limit = requestedLoadSize, page = key, max_id = key
)
}
}
@ -135,6 +140,20 @@ open class AccountListFragment : FeedFragment() {
override fun onResponse(call: Call<List<Account>>, response: Response<List<Account>>) {
if (response.isSuccessful && response.body() != null) {
val data = response.body()!!
if(response.headers()["Link"] != null){
//Header is of the form:
// Link: <https://mastodon.social/api/v1/accounts/1/followers?limit=2&max_id=7628164>; rel="next", <https://mastodon.social/api/v1/accounts/1/followers?limit=2&since_id=7628165>; rel="prev"
// So we want the first max_id value. In case there are arguments after
// the max_id in the URL, we make sure to stop at the first '?'
currentPage = response.headers()["Link"]
.orEmpty()
.substringAfter("max_id=")
.substringBefore('?')
.substringBefore('>')
.toIntOrNull() ?: 0
} else {
currentPage++
}
callback.onResult(data)
} else{
showError()

View File

@ -9,6 +9,7 @@ import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.ProgressBar
import androidx.annotation.StringRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.MutableLiveData
import androidx.paging.DataSource
@ -70,12 +71,15 @@ open class FeedFragment: Fragment() {
}
fun showError(@StringRes errorText: Int = R.string.loading_toast, show: Boolean = true){
val errorLayout = view?.findViewById<ConstraintLayout>(R.id.errorLayout)
val progressBar = view?.findViewById<ProgressBar>(R.id.progressBar)
if(show){
errorLayout.visibility = VISIBLE
progressBar.visibility = GONE
errorLayout?.visibility = VISIBLE
progressBar?.visibility = GONE
} else {
errorLayout.visibility = GONE
progressBar.visibility = VISIBLE
errorLayout?.visibility = GONE
progressBar?.visibility = VISIBLE
}
}

View File

@ -42,18 +42,18 @@ class SearchAccountFragment: AccountListFragment(){
}
override fun getKey(item: Account): String {
return item.id
return content.value?.loadedCount.toString()
}
private fun searchMakeInitialCall(requestedLoadSize: Int): Call<Results> {
return pixelfedAPI
.search("Bearer $accessToken",
limit="$requestedLoadSize", q=query,
limit="$requestedLoadSize", q = query,
type = Results.SearchType.accounts)
}
private fun searchMakeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
return pixelfedAPI
.search("Bearer $accessToken", max_id=key,
.search("Bearer $accessToken", offset = key,
limit="$requestedLoadSize", q = query,
type = Results.SearchType.accounts)
}
@ -75,7 +75,6 @@ class SearchAccountFragment: AccountListFragment(){
if (response.code() == 200) {
val notifications = response.body()!!.accounts as ArrayList<Account>
callback.onResult(notifications as List<Account>)
} else{
showError()
}

View File

@ -29,7 +29,7 @@ class SearchHashtagFragment: FeedFragment(){
private lateinit var query: String
private lateinit var content: LiveData<PagedList<Tag>>
protected lateinit var adapter : TagsRecyclerViewAdapter
private lateinit var adapter : TagsRecyclerViewAdapter
lateinit var factory: FeedDataSourceFactory<Int, Tag>
@ -80,7 +80,7 @@ class SearchHashtagFragment: FeedFragment(){
}
private fun searchMakeAfterCall(requestedLoadSize: Int, key: Int): Call<Results> {
return pixelfedAPI
.search("Bearer $accessToken", offset=key,
.search("Bearer $accessToken", offset = key.toString(),
limit="$requestedLoadSize", q = query,
type = Results.SearchType.hashtags)
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z"
android:fillColor="#000000"/>
</vector>

View File

@ -2,6 +2,7 @@
<androidx.gridlayout.widget.GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:columnCount="3">
@ -9,19 +10,35 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/postPreview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="TODO"
android:padding="1dp"
app:layout_columnWeight="1"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_gravity="fill"
app:layout_rowWeight="1"
app:layout_columnWeight="1"/>
tools:srcCompat="@tools:sample/avatars" />
<ImageView
android:id="@+id/albumIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:foreground="@drawable/album_black_24dp"
android:foregroundGravity="center"
android:foregroundTint="#FFFFFF"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.gridlayout.widget.GridLayout>

View File

@ -104,6 +104,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/discoverText" />
</androidx.constraintlayout.motion.widget.MotionLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>