Follow button and followers list (#109)
* wip follow button * Fixed follow api typo * Changed follow button text * FollowButton click animated * Fixed follow button * FollowButton hidden if not ready * Added toats * Removed useless val * Refactored follow onClickListener * Merge with master * Followers list WIP * Distinguish followers and following * Fixed typo * Open followers list * Follows list done * Tests failing * Fixed follow button test * Completed url followers * Fixed tests * Added test
This commit is contained in:
parent
5decd6ae12
commit
3506f034d4
|
@ -56,6 +56,83 @@ class MockedServerTest {
|
|||
onView(withId(R.id.nbFollowersTextView)).check(matches(withText("68\nFollowers")))
|
||||
onView(withId(R.id.accountNameTextView)).check(matches(withText("deerbard_photo")))
|
||||
}
|
||||
// WIP TEST
|
||||
@Test
|
||||
fun clickFollowButton() {
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
Thread.sleep(1000)
|
||||
|
||||
//Get initial like count
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<PostViewHolder>
|
||||
(0, clickChildViewWithId(R.id.username)))
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
||||
// Unfollow
|
||||
onView(withId(R.id.followButton)).perform((ViewActions.click()))
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.followButton)).check(matches(withText("Follow")))
|
||||
|
||||
// Follow
|
||||
onView(withId(R.id.followButton)).perform((ViewActions.click()))
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.followButton)).check(matches(withText("Unfollow")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clickFollowers() {
|
||||
ActivityScenario.launch(MainActivity::class.java).onActivity{
|
||||
a -> a.findViewById<TabLayout>(R.id.tabs).getTabAt(4)?.select()
|
||||
}
|
||||
Thread.sleep(1000)
|
||||
// Open followers list
|
||||
onView(withId(R.id.nbFollowersTextView)).perform((ViewActions.click()))
|
||||
Thread.sleep(1000)
|
||||
// Open follower's profile
|
||||
onView(withText("ete2")).perform((ViewActions.click()))
|
||||
Thread.sleep(1000)
|
||||
|
||||
onView(withId(R.id.accountNameTextView)).check(matches(withText("ete2")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clickOtherUserFollowers() {
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
Thread.sleep(1000)
|
||||
|
||||
//Get initial like count
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<PostViewHolder>
|
||||
(0, clickChildViewWithId(R.id.username)))
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
||||
// Open followers list
|
||||
onView(withId(R.id.nbFollowersTextView)).perform((ViewActions.click()))
|
||||
Thread.sleep(1000)
|
||||
// Open follower's profile
|
||||
onView(withText("ete2")).perform((ViewActions.click()))
|
||||
Thread.sleep(1000)
|
||||
|
||||
onView(withId(R.id.accountNameTextView)).check(matches(withText("ete2")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clickFollowing() {
|
||||
ActivityScenario.launch(MainActivity::class.java).onActivity{
|
||||
a -> a.findViewById<TabLayout>(R.id.tabs).getTabAt(4)?.select()
|
||||
}
|
||||
Thread.sleep(1000)
|
||||
// Open followers list
|
||||
onView(withId(R.id.nbFollowingTextView)).perform((ViewActions.click()))
|
||||
Thread.sleep(1000)
|
||||
// Open following's profile
|
||||
onView(withText("Dobios")).perform((ViewActions.click()))
|
||||
Thread.sleep(1000)
|
||||
|
||||
onView(withId(R.id.accountNameTextView)).check(matches(withText("Dobios")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotificationsList() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.h.pixeldroid.testUtility
|
||||
|
||||
import com.h.pixeldroid.objects.Account
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.mockwebserver.Dispatcher
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
|
@ -128,6 +127,11 @@ class MockServer {
|
|||
|
||||
val reblogJson = """{"id":"156491373246287872","created_at":"2020-04-16T20:00:50.000000Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","url":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","replies_count":1,"reblogs_count":14,"favourites_count":2,"reblogged":true,"favourited":false,"muted":false,"bookmarked":false,"pinned":false,"content":"<a class=\"u-url mention\" href=\"https:\/\/pixelfed.de\/Dobios\" rel=\"external nofollow noopener\">@Dobios<\/a> <a class=\"u-url mention\" href=\"https:\/\/pixelfed.de\/Dante\" rel=\"external nofollow noopener\">@Dante<\/a>","reblog":null,"application":{"name":"web","website":null},"mentions":[{"id":"136800034732773376","url":"https:\/\/pixelfed.de\/Dobios","username":"Dobios","acct":"Dobios"},{"id":"136453537340198912","url":"https:\/\/pixelfed.de\/dante","username":"dante","acct":"dante"}],"tags":[{"name":"mushroom","url":"https:\/\/pixelfed.de\/discover\/tags\/mushroom"},{"name":"commentsstillbroken","url":"https:\/\/pixelfed.de\/discover\/tags\/commentsstillbroken"},{"name":"fixyourapi","url":"https:\/\/pixelfed.de\/discover\/tags\/fixyourapi"},{"name":"pls","url":"https:\/\/pixelfed.de\/discover\/tags\/pls"}],"emojis":[],"card":null,"poll":null,"account":{"id":"145183325781364736","username":"machintuck","acct":"machintuck","display_name":"Arthur","locked":false,"created_at":"2020-03-16T15:06:42.000000Z","followers_count":4,"following_count":4,"statuses_count":5,"note":"","url":"https:\/\/pixelfed.de\/machintuck","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"19228","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP_thumb.jpeg","text_url":null,"meta":null,"description":null}]}"""
|
||||
|
||||
val followRelationshipJson = """{"id":"136800034732773376","following":true,"followed_by":true,"blocking":false,"muting":false,"muting_notifications":null,"requested":false,"domain_blocking":null,"showing_reblogs":null,"endorsed":false}"""
|
||||
val unfollowRelationshipJson = """{"id":"136800034732773376","following":false,"followed_by":true,"blocking":false,"muting":false,"muting_notifications":null,"requested":false,"domain_blocking":null,"showing_reblogs":null,"endorsed":false}"""
|
||||
val relationshipJson = """[{"id":"136800034732773376","following":true,"followed_by":true,"blocking":false,"muting":false,"muting_notifications":null,"requested":false,"domain_blocking":null,"showing_reblogs":null,"endorsed":false}]"""
|
||||
val followersJson = """[{"id":"118664651939647488","username":"ete2","acct":"ete2","display_name":"Christian","locked":false,"created_at":"2020-01-03T10:50:57.000000Z","followers_count":22,"following_count":3,"statuses_count":20,"note":"Nature lover - hobby photographer","url":"https:\/\/pixelfed.de\/ete2","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/011\/866\/465\/193\/964\/748\/8\/pUY03jBlOeOqjNDMWwYM_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/011\/866\/465\/193\/964\/748\/8\/pUY03jBlOeOqjNDMWwYM_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},{"id":"136800034732773376","username":"Dobios","acct":"Dobios","display_name":"Andrew Dobis","locked":false,"created_at":"2020-02-22T11:54:29.000000Z","followers_count":7,"following_count":5,"statuses_count":3,"note":"","url":"https:\/\/pixelfed.de\/Dobios","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/013\/680\/003\/473\/277\/337\/6\/AnBpDi92CAuuNjOYkyqg_avatar.jpeg?v=4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/013\/680\/003\/473\/277\/337\/6\/AnBpDi92CAuuNjOYkyqg_avatar.jpeg?v=4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},{"id":"144813993922531328","username":"Clement","acct":"Clement","display_name":"Andrea","locked":false,"created_at":"2020-03-15T14:39:06.000000Z","followers_count":2,"following_count":4,"statuses_count":0,"note":"","url":"https:\/\/pixelfed.de\/Clement","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/default.png?v=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false}]"""
|
||||
val followersAfterJson = """[]"""
|
||||
|
||||
fun start() {
|
||||
server.dispatcher = getDispatcher()
|
||||
|
@ -223,6 +227,48 @@ class MockServer {
|
|||
"application/json; charset=utf-8"
|
||||
).setResponseCode(200).setBody(unlikeJson)
|
||||
}
|
||||
request.path?.matches("/api/v1/accounts/[0-9]*/follow".toRegex()) == true -> {
|
||||
return MockResponse().addHeader(
|
||||
"Content-Type",
|
||||
"application/json; charset=utf-8"
|
||||
).setResponseCode(200).setBody(followRelationshipJson)
|
||||
}
|
||||
request.path?.matches("/api/v1/accounts/[0-9]*/unfollow".toRegex()) == true -> {
|
||||
return MockResponse().addHeader(
|
||||
"Content-Type",
|
||||
"application/json; charset=utf-8"
|
||||
).setResponseCode(200).setBody(unfollowRelationshipJson)
|
||||
}
|
||||
request.path?.startsWith("/api/v1/accounts/relationships") == true -> {
|
||||
return MockResponse().addHeader(
|
||||
"Content-Type",
|
||||
"application/json; charset=utf-8"
|
||||
).setResponseCode(200).setBody(relationshipJson)
|
||||
}
|
||||
request.path?.matches("/api/v1/accounts/[0-9]*/followers\\?limit=[0-9]*".toRegex()) == true -> {
|
||||
return MockResponse().addHeader(
|
||||
"Content-Type",
|
||||
"application/json; charset=utf-8"
|
||||
).setResponseCode(200).setBody(followersJson)
|
||||
}
|
||||
request.path?.matches("/api/v1/accounts/[0-9]*/followers\\?since_id=[0-9]*&limit=[0-9]*".toRegex()) == true -> {
|
||||
return MockResponse().addHeader(
|
||||
"Content-Type",
|
||||
"application/json; charset=utf-8"
|
||||
).setResponseCode(200).setBody(followersAfterJson)
|
||||
}
|
||||
request.path?.matches("/api/v1/accounts/[0-9]*/following\\?limit=[0-9]*".toRegex()) == true -> {
|
||||
return MockResponse().addHeader(
|
||||
"Content-Type",
|
||||
"application/json; charset=utf-8"
|
||||
).setResponseCode(200).setBody(followersJson)
|
||||
}
|
||||
request.path?.matches("/api/v1/accounts/[0-9]*/following\\?since_id=[0-9]*&limit=[0-9]*".toRegex()) == true -> {
|
||||
return MockResponse().addHeader(
|
||||
"Content-Type",
|
||||
"application/json; charset=utf-8"
|
||||
).setResponseCode(200).setBody(followersAfterJson)
|
||||
}
|
||||
else -> return MockResponse().setResponseCode(404)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".PostActivity"/>
|
||||
<activity android:name=".ProfileActivity"/>
|
||||
|
||||
<activity android:name=".FollowsActivity" />
|
||||
<activity android:name=".PostActivity" />
|
||||
<activity android:name=".ProfileActivity" />
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:label="@string/title_activity_settings2">
|
||||
|
@ -34,8 +34,8 @@
|
|||
</activity>
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package com.h.pixeldroid
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.fragments.feeds.FollowsFragment
|
||||
import com.h.pixeldroid.objects.Account
|
||||
import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_ID_TAG
|
||||
import com.h.pixeldroid.objects.Account.Companion.FOLLOWING_TAG
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
class FollowsActivity : AppCompatActivity() {
|
||||
var followsFragment = FollowsFragment()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_followers)
|
||||
|
||||
// Get account id
|
||||
val id = intent.getSerializableExtra(ACCOUNT_ID_TAG) as String?
|
||||
val following = intent.getSerializableExtra(FOLLOWING_TAG) as Boolean
|
||||
|
||||
if(id == null) {
|
||||
val preferences = this.getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
val pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
val accessToken = preferences.getString("accessToken", "")
|
||||
|
||||
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
|
||||
launchActivity(id, following)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
launchActivity(id, following)
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchActivity(id : String, following : Boolean) {val arguments = Bundle()
|
||||
arguments.putSerializable(ACCOUNT_ID_TAG, id)
|
||||
arguments.putSerializable(FOLLOWING_TAG, following)
|
||||
followsFragment.arguments = arguments
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(R.id.followsFragment, followsFragment).commit()
|
||||
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ class ProfileActivity : AppCompatActivity() {
|
|||
|
||||
profileFragment = ProfileFragment()
|
||||
val arguments = Bundle()
|
||||
arguments.putSerializable("profileTag", account)
|
||||
arguments.putSerializable(ACCOUNT_TAG, account)
|
||||
profileFragment.arguments = arguments
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
|
|
|
@ -38,11 +38,28 @@ interface PixelfedAPI {
|
|||
@Field("grant_type") grant_type: String? = null
|
||||
): Call<Token>
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/accounts/{id}/follow")
|
||||
fun follow(
|
||||
//The authorization header needs to be of the form "Bearer <token>"
|
||||
@Path("id") statusId: String,
|
||||
@Header("Authorization") authorization: String,
|
||||
@Field("reblogs") reblogs : Boolean = true
|
||||
) : Call<Relationship>
|
||||
|
||||
@POST("/api/v1/accounts/{id}/unfollow")
|
||||
fun unfollow(
|
||||
//The authorization header needs to be of the form "Bearer <token>"
|
||||
@Path("id") statusId: String,
|
||||
@Header("Authorization") authorization: String
|
||||
) : Call<Relationship>
|
||||
|
||||
@POST("api/v1/statuses/{id}/favourite")
|
||||
fun likePost(
|
||||
//The authorization header needs to be of the form "Bearer <token>"
|
||||
@Header("Authorization") authorization: String,
|
||||
@Path("id") statusId: String
|
||||
|
||||
) : Call<Status>
|
||||
|
||||
@POST("/api/v1/statuses/{id}/unfavourite")
|
||||
|
@ -147,6 +164,30 @@ interface PixelfedAPI {
|
|||
@Path("id") account_id: String? = null
|
||||
): Call<List<Status>>
|
||||
|
||||
@GET("/api/v1/accounts/relationships")
|
||||
fun checkRelationships(
|
||||
@Header("Authorization") authorization : String,
|
||||
@Query("id[]") account_ids : List<String>
|
||||
) : Call<List<Relationship>>
|
||||
|
||||
@GET("/api/v1/accounts/{id}/followers")
|
||||
fun followers(
|
||||
@Path("id") account_id: String,
|
||||
@Header("Authorization") authorization: String,
|
||||
@Query("max_id") max_id: String? = null,
|
||||
@Query("since_id") since_id: String? = null,
|
||||
@Query("limit") limit: Number? = null
|
||||
) : Call<List<Account>>
|
||||
|
||||
@GET("/api/v1/accounts/{id}/following")
|
||||
fun following(
|
||||
@Path("id") account_id: String,
|
||||
@Header("Authorization") authorization: String,
|
||||
@Query("max_id") max_id: String? = null,
|
||||
@Query("since_id") since_id: String? = null,
|
||||
@Query("limit") limit: Number? = 40
|
||||
) : Call<List<Account>>
|
||||
|
||||
@GET("/api/v1/accounts/{id}")
|
||||
fun getAccount(
|
||||
@Header("Authorization") authorization: String,
|
||||
|
|
|
@ -11,22 +11,30 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.FollowsActivity
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.objects.Account
|
||||
import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_ID_TAG
|
||||
import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_TAG
|
||||
import com.h.pixeldroid.objects.Account.Companion.FOLLOWING_TAG
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import kotlinx.android.synthetic.main.profile_fragment.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
|
||||
class ProfileFragment : Fragment() {
|
||||
private lateinit var preferences: SharedPreferences
|
||||
private lateinit var preferences : SharedPreferences
|
||||
private lateinit var pixelfedAPI : PixelfedAPI
|
||||
private lateinit var adapter : ProfilePostsRecyclerViewAdapter
|
||||
private lateinit var recycler : RecyclerView
|
||||
private var accessToken : String? = null
|
||||
private var account: Account? = null
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -36,19 +44,9 @@ class ProfileFragment : Fragment() {
|
|||
preferences = this.requireActivity().getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
account = arguments?.getSerializable("profileTag") as Account?
|
||||
account = arguments?.getSerializable(ACCOUNT_TAG) as Account?
|
||||
val view = inflater.inflate(R.layout.profile_fragment, container, false)
|
||||
|
||||
if(account == null) {
|
||||
// Edit button redirects to Pixelfed's "edit account" page
|
||||
val editButton: Button = view.findViewById(R.id.editButton)
|
||||
editButton.visibility = View.VISIBLE
|
||||
val followButton: Button = view.findViewById(R.id.followButton)
|
||||
followButton.visibility = View.GONE
|
||||
|
||||
editButton.setOnClickListener((View.OnClickListener { onClickEditButton() }))
|
||||
}
|
||||
|
||||
// Set RecyclerView as a grid with 3 columns
|
||||
recycler = view.findViewById(R.id.profilePostsRecyclerView)
|
||||
recycler.layoutManager = GridLayoutManager(context, 3)
|
||||
|
@ -61,8 +59,13 @@ class ProfileFragment : Fragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
val accessToken = preferences.getString("accessToken", "")
|
||||
pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
accessToken = preferences.getString("accessToken", "")
|
||||
|
||||
// On click open followers list
|
||||
view.nbFollowersTextView.setOnClickListener{ onClickFollowers() }
|
||||
// On click open followers list
|
||||
view.nbFollowingTextView.setOnClickListener{ onClickFollowing() }
|
||||
|
||||
if(account == null) {
|
||||
pixelfedAPI.verifyCredentials("Bearer $accessToken")
|
||||
|
@ -82,16 +85,20 @@ class ProfileFragment : Fragment() {
|
|||
Log.e("ProfileFragment:", t.toString())
|
||||
}
|
||||
})
|
||||
|
||||
// Edit button redirects to Pixelfed's "edit account" page
|
||||
val editButton: Button = view.findViewById(R.id.editButton)
|
||||
editButton.visibility = View.VISIBLE
|
||||
editButton.setOnClickListener{ onClickEditButton() }
|
||||
|
||||
} else {
|
||||
account!!.activateFollow(view, requireContext(), pixelfedAPI, accessToken)
|
||||
account!!.setContent(view)
|
||||
setPosts(account!!)
|
||||
}
|
||||
}
|
||||
// Populate profile page with user's posts
|
||||
private fun setPosts(account: Account) {
|
||||
val pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
val accessToken = preferences.getString("accessToken", "")
|
||||
|
||||
pixelfedAPI.accountPosts("Bearer $accessToken", account_id = account.id).enqueue(object :
|
||||
Callback<List<Status>> {
|
||||
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
|
||||
|
@ -124,4 +131,20 @@ class ProfileFragment : Fragment() {
|
|||
Log.e("ProfileFragment", text)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onClickFollowers() {
|
||||
val intent = Intent(context, FollowsActivity::class.java)
|
||||
intent.putExtra(FOLLOWING_TAG, true)
|
||||
intent.putExtra(ACCOUNT_ID_TAG, account?.id)
|
||||
|
||||
ContextCompat.startActivity(requireContext(), intent, null)
|
||||
}
|
||||
|
||||
private fun onClickFollowing() {
|
||||
val intent = Intent(context, FollowsActivity::class.java)
|
||||
intent.putExtra(FOLLOWING_TAG, false)
|
||||
intent.putExtra(ACCOUNT_ID_TAG, account?.id)
|
||||
|
||||
ContextCompat.startActivity(requireContext(), intent, null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
package com.h.pixeldroid.fragments.feeds
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.paging.LivePagedListBuilder
|
||||
import androidx.paging.PagedList
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.ListPreloader
|
||||
import com.bumptech.glide.RequestBuilder
|
||||
import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.util.ViewPreloadSizeProvider
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.objects.Account
|
||||
import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_ID_TAG
|
||||
import com.h.pixeldroid.objects.Account.Companion.FOLLOWING_TAG
|
||||
import kotlinx.android.synthetic.main.fragment_follows.view.*
|
||||
import retrofit2.Call
|
||||
|
||||
class FollowsFragment : FeedFragment<Account, FollowsFragment.FollowsRecyclerViewAdapter.ViewHolder>() {
|
||||
lateinit var profilePicRequest: RequestBuilder<Drawable>
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
//RequestBuilder that is re-used for every image
|
||||
profilePicRequest = Glide.with(this)
|
||||
.asDrawable().apply(RequestOptions().circleCrop())
|
||||
.placeholder(R.drawable.ic_default_user)
|
||||
|
||||
adapter = FollowsRecyclerViewAdapter()
|
||||
list.adapter = adapter
|
||||
|
||||
//Make Glide be aware of the recyclerview and pre-load images
|
||||
val sizeProvider: ListPreloader.PreloadSizeProvider<Account> = ViewPreloadSizeProvider()
|
||||
val preloader: RecyclerViewPreloader<Account> = RecyclerViewPreloader(
|
||||
Glide.with(this), adapter, sizeProvider, 4
|
||||
)
|
||||
list.addOnScrollListener(preloader)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val id = arguments?.getSerializable(ACCOUNT_ID_TAG) as String
|
||||
val following = arguments?.getSerializable(FOLLOWING_TAG) as Boolean
|
||||
content = makeContent(id, following)
|
||||
|
||||
content.observe(viewLifecycleOwner,
|
||||
Observer { c ->
|
||||
adapter.submitList(c)
|
||||
//after a refresh is done we need to stop the pull to refresh spinner
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
})
|
||||
}
|
||||
|
||||
private fun makeContent(id : String, following : Boolean) : LiveData<PagedList<Account>> {
|
||||
fun makeInitialCall(requestedLoadSize: Int): Call<List<Account>> {
|
||||
if(following) {
|
||||
return pixelfedAPI.followers(id, "Bearer $accessToken",
|
||||
limit = requestedLoadSize)
|
||||
} else {
|
||||
return pixelfedAPI.following(id, "Bearer $accessToken",
|
||||
limit = requestedLoadSize)
|
||||
}
|
||||
}
|
||||
fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Account>> {
|
||||
if(following) {
|
||||
return pixelfedAPI.followers(id, "Bearer $accessToken",
|
||||
since_id = key, limit = requestedLoadSize)
|
||||
} else {
|
||||
return pixelfedAPI.following(id, "Bearer $accessToken",
|
||||
since_id = key, limit = requestedLoadSize)
|
||||
}
|
||||
}
|
||||
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
|
||||
factory = FeedDataSourceFactory(::makeInitialCall, ::makeAfterCall)
|
||||
return LivePagedListBuilder(factory, config).build()
|
||||
}
|
||||
|
||||
inner class FollowsRecyclerViewAdapter : FeedsRecyclerViewAdapter<Account,FollowsRecyclerViewAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.fragment_follows, parent, false)
|
||||
context = view.context
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder : ViewHolder, position : Int) {
|
||||
val account = getItem(position) ?: return
|
||||
profilePicRequest.load(account.avatar_static).into(holder.avatar)
|
||||
|
||||
holder.username.text = account.username
|
||||
|
||||
holder.mView.setOnClickListener { account.openProfile(context) }
|
||||
}
|
||||
|
||||
inner class ViewHolder(val mView : View) : RecyclerView.ViewHolder(mView) {
|
||||
val avatar : ImageView = mView.follows_avatar
|
||||
val username : TextView = mView.follows_username
|
||||
}
|
||||
|
||||
override fun getPreloadItems(position : Int) : MutableList<Account> {
|
||||
val account = getItem(position) ?: return mutableListOf()
|
||||
return mutableListOf(account)
|
||||
}
|
||||
|
||||
override fun getPreloadRequestBuilder(item : Account) : RequestBuilder<*>? {
|
||||
return profilePicRequest.load(item.avatar_static)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,11 +7,13 @@ import android.util.Log
|
|||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.h.pixeldroid.ProfileActivity
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import kotlinx.android.synthetic.main.profile_fragment.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
@ -24,7 +26,7 @@ https://docs.joinmastodon.org/entities/account/
|
|||
|
||||
data class Account(
|
||||
//Base attributes
|
||||
val id: String,
|
||||
override val id: String,
|
||||
val username: String,
|
||||
val acct: String,
|
||||
val url: String, //HTTPS URL
|
||||
|
@ -48,9 +50,11 @@ data class Account(
|
|||
val fields: List<Field>? = emptyList(),
|
||||
val bot: Boolean = false,
|
||||
val source: Source? = null
|
||||
) : Serializable {
|
||||
) : Serializable, FeedContent() {
|
||||
companion object {
|
||||
const val ACCOUNT_TAG = "AccountTag"
|
||||
const val ACCOUNT_ID_TAG = "AccountIdTag"
|
||||
const val FOLLOWING_TAG = "FollowingTag"
|
||||
|
||||
/**
|
||||
* @brief Opens an activity of the profile withn the given id
|
||||
|
@ -83,9 +87,10 @@ data class Account(
|
|||
// Open profile activity with given account
|
||||
fun openProfile(context: Context) {
|
||||
val intent = Intent(context, ProfileActivity::class.java)
|
||||
intent.putExtra(Account.ACCOUNT_TAG, this)
|
||||
intent.putExtra(ACCOUNT_TAG, this)
|
||||
startActivity(context, intent, null)
|
||||
}
|
||||
|
||||
// Populate myProfile page with user's data
|
||||
fun setContent(view: View) {
|
||||
val profilePicture = view.findViewById<ImageView>(R.id.profilePictureImageView)
|
||||
|
@ -110,5 +115,86 @@ data class Account(
|
|||
nbFollowing.text = "${this.following_count}\nFollowing"
|
||||
nbFollowing.setTypeface(null, Typeface.BOLD)
|
||||
}
|
||||
}
|
||||
|
||||
// Activate follow button
|
||||
fun activateFollow(
|
||||
view : View,
|
||||
context : Context,
|
||||
api : PixelfedAPI,
|
||||
credential : String?
|
||||
) {
|
||||
// Get relationship between the two users (credential and this) and set followButton accordingly
|
||||
api.checkRelationships("Bearer $credential", listOf(id)).enqueue(object : Callback<List<Relationship>> {
|
||||
override fun onFailure(call: Call<List<Relationship>>, t: Throwable) {
|
||||
Log.e("FOLLOW ERROR", t.toString())
|
||||
Toast.makeText(context,"Could not get follow status", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call<List<Relationship>>, response: Response<List<Relationship>>) {
|
||||
if(response.code() == 200) {
|
||||
if(response.body()!!.isNotEmpty()) {
|
||||
if (response.body()!![0].following) {
|
||||
view.followButton.text = "Unfollow"
|
||||
setOnClickUnfollow(view, api, context, credential)
|
||||
} else {
|
||||
view.followButton.text = "Follow"
|
||||
setOnClickFollow(view, api, context, credential)
|
||||
}
|
||||
view.followButton.visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(context, "Could not display follow button", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setOnClickFollow(view: View, api: PixelfedAPI, context: Context, credential: String?) {
|
||||
view.followButton.setOnClickListener {
|
||||
api.follow(id, "Bearer $credential").enqueue(object : Callback<Relationship> {
|
||||
override fun onFailure(call: Call<Relationship>, t: Throwable) {
|
||||
Log.e("FOLLOW ERROR", t.toString())
|
||||
Toast.makeText(context, "Could not follow", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<Relationship>,
|
||||
response: Response<Relationship>
|
||||
) {
|
||||
if (response.code() == 200) {
|
||||
view.followButton.text = "Unfollow"
|
||||
setOnClickUnfollow(view, api, context, credential)
|
||||
} else if (response.code() == 403) {
|
||||
Toast.makeText(context, "This action is not allowed", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun setOnClickUnfollow(view: View, api: PixelfedAPI, context: Context, credential: String?) {
|
||||
view.followButton.setOnClickListener {
|
||||
api.unfollow(id, "Bearer $credential").enqueue(object : Callback<Relationship> {
|
||||
override fun onFailure(call: Call<Relationship>, t: Throwable) {
|
||||
Log.e("UNFOLLOW ERROR", t.toString())
|
||||
Toast.makeText(context, "Could not unfollow", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<Relationship>,
|
||||
response: Response<Relationship>
|
||||
) {
|
||||
if (response.code() == 200) {
|
||||
view.followButton.text = "Follow"
|
||||
setOnClickFollow(view, api, context, credential)
|
||||
} else if (response.code() == 401) {
|
||||
Toast.makeText(context, "The access token is invalid", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.h.pixeldroid.objects
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class Relationship(
|
||||
// Required atributes
|
||||
val id: String,
|
||||
val following: Boolean,
|
||||
val requested: Boolean,
|
||||
val endorsed: Boolean,
|
||||
val followed_by: Boolean,
|
||||
val muting: Boolean,
|
||||
val muting_notifications: Boolean,
|
||||
val showing_reblogs: Boolean,
|
||||
val blocking: Boolean,
|
||||
val domain_blocking: Boolean,
|
||||
val blocked_by: Boolean
|
||||
) : Serializable
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/followsFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".FollowsActivity">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/follows_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_default_user" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/follows_username"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="28dp"
|
||||
android:text="TextView"
|
||||
app:layout_constraintStart_toEndOf="@+id/follows_avatar"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -6,8 +6,7 @@
|
|||
android:name="com.h.pixeldroid.fragments.ProfilePostsFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_margin="16dp"
|
||||
app:layoutManager="LinearLayoutManager"
|
||||
tools:context=".fragments.ProfilePostsFragment"
|
||||
tools:listitem="@layout/fragment_profile_posts" />
|
|
@ -11,7 +11,6 @@
|
|||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearlayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
@ -35,7 +34,6 @@
|
|||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="10"
|
||||
|
@ -110,11 +108,11 @@
|
|||
|
||||
<Button
|
||||
android:id="@+id/followButton"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="30dp"
|
||||
android:background="@color/browser_actions_divider_color"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Follow"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
@ -135,7 +133,6 @@
|
|||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
|
@ -164,9 +161,7 @@
|
|||
android:id="@+id/profilePostsRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_margin="5dp"
|
||||
app:layoutManager="LinearLayoutManager"
|
||||
tools:context=".fragments.ProfileFragment"
|
||||
tools:listitem="@layout/fragment_profile_posts" />
|
||||
|
|
Loading…
Reference in New Issue