Discover feature (#143)
* Discover feature * Add refresh handler * Add json to mockserver * singleton mock server, fix null items * Add test to open discover post
This commit is contained in:
parent
0038cd8c9e
commit
663d0e2599
|
@ -76,6 +76,17 @@ class MockedServerTest {
|
|||
Thread.sleep(3000)
|
||||
onView(first(withId(R.id.tag_name))).check(matches(withText("#caturday")))
|
||||
|
||||
}
|
||||
@Test
|
||||
fun openDiscoverPost(){
|
||||
activityScenario.onActivity{
|
||||
a -> a.findViewById<TabLayout>(R.id.tabs).getTabAt(1)?.select()
|
||||
}
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.discoverList)).perform(click())
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.username)).check(matches(withText("machintuck")))
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,14 +1,25 @@
|
|||
package com.h.pixeldroid
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.fragments.PostFragment
|
||||
import com.h.pixeldroid.objects.DiscoverPost
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.objects.Status.Companion.DISCOVER_TAG
|
||||
import com.h.pixeldroid.objects.Status.Companion.DOMAIN_TAG
|
||||
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
|
||||
import kotlinx.android.synthetic.main.activity_post.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
class PostActivity : AppCompatActivity() {
|
||||
private lateinit var preferences: SharedPreferences
|
||||
lateinit var postFragment : PostFragment
|
||||
lateinit var domain : String
|
||||
|
||||
|
@ -17,18 +28,53 @@ class PostActivity : AppCompatActivity() {
|
|||
setContentView(R.layout.activity_post)
|
||||
|
||||
val status = intent.getSerializableExtra(POST_TAG) as Status?
|
||||
val discoverPost: DiscoverPost? = intent.getSerializableExtra(DISCOVER_TAG) as DiscoverPost?
|
||||
|
||||
domain = getSharedPreferences(
|
||||
preferences = getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
).getString("domain", "")!!
|
||||
)
|
||||
domain = preferences.getString("domain", "")!!
|
||||
|
||||
postFragment = PostFragment()
|
||||
val arguments = Bundle()
|
||||
arguments.putSerializable(POST_TAG, status)
|
||||
arguments.putString(DOMAIN_TAG, domain)
|
||||
postFragment.arguments = arguments
|
||||
|
||||
if (discoverPost != null) {
|
||||
postProgressBar.visibility = View.VISIBLE
|
||||
getDiscoverPost(arguments, discoverPost)
|
||||
} else {
|
||||
initializeFragment(arguments, status)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDiscoverPost(
|
||||
arguments: Bundle,
|
||||
discoverPost: DiscoverPost
|
||||
) {
|
||||
val api = PixelfedAPI.create(domain)
|
||||
val accessToken = preferences.getString("accessToken", "") ?: ""
|
||||
val id = discoverPost.url?.substringAfterLast('/') ?: ""
|
||||
api.getStatus("Bearer $accessToken", id).enqueue(object : Callback<Status> {
|
||||
|
||||
override fun onFailure(call: Call<Status>, t: Throwable) {
|
||||
Log.e("PostActivity:", t.toString())
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call<Status>, response: Response<Status>) {
|
||||
if(response.code() == 200) {
|
||||
val status = response.body()!!
|
||||
postProgressBar.visibility = View.GONE
|
||||
initializeFragment(arguments, status)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initializeFragment(arguments: Bundle, status: Status?){
|
||||
arguments.putSerializable(POST_TAG, status)
|
||||
postFragment.arguments = arguments
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(R.id.postFragmentSingle, postFragment).commit()
|
||||
postFragmentSingle.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class ProfileActivity : AppCompatActivity() {
|
|||
// Set posts RecyclerView as a grid with 3 columns
|
||||
recycler = findViewById(R.id.profilePostsRecyclerView)
|
||||
recycler.layoutManager = GridLayoutManager(applicationContext, 3)
|
||||
adapter = ProfilePostsRecyclerViewAdapter(this)
|
||||
adapter = ProfilePostsRecyclerViewAdapter()
|
||||
recycler.adapter = adapter
|
||||
|
||||
setContent()
|
||||
|
@ -131,12 +131,8 @@ class ProfileActivity : AppCompatActivity() {
|
|||
|
||||
override fun onResponse(call: Call<List<Status>>, response: Response<List<Status>>) {
|
||||
if(response.code() == 200) {
|
||||
val posts = ArrayList<Status>()
|
||||
val statuses = response.body()!!
|
||||
for(status in statuses) {
|
||||
posts.add(status)
|
||||
}
|
||||
adapter.addPosts(posts)
|
||||
adapter.addPosts(statuses)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -18,6 +18,17 @@ import retrofit2.http.Field
|
|||
|
||||
interface PixelfedAPI {
|
||||
|
||||
companion object {
|
||||
fun create(baseUrl: String): PixelfedAPI {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
|
||||
.build().create(PixelfedAPI::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/apps")
|
||||
fun registerApplication(
|
||||
|
@ -210,15 +221,11 @@ interface PixelfedAPI {
|
|||
@Path("id") accountId : String
|
||||
): Call<Account>
|
||||
|
||||
companion object {
|
||||
fun create(baseUrl: String): PixelfedAPI {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
|
||||
.build().create(PixelfedAPI::class.java)
|
||||
}
|
||||
}
|
||||
@GET("/api/v1/statuses/{id}")
|
||||
fun getStatus(
|
||||
@Header("Authorization") authorization: String,
|
||||
@Path("id") accountId : String
|
||||
): Call<Status>
|
||||
|
||||
@Multipart
|
||||
@POST("/api/v1/media")
|
||||
|
@ -231,5 +238,11 @@ interface PixelfedAPI {
|
|||
// get instance configuration
|
||||
@GET("/api/v1/instance")
|
||||
fun instance() : Call<Instance>
|
||||
|
||||
// get discover
|
||||
@GET("/api/v2/discover/posts")
|
||||
fun discover(
|
||||
@Header("Authorization") authorization: String
|
||||
) : Call<DiscoverPosts>
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class ProfilePostsFragment : Fragment() {
|
|||
columnCount <= 1 -> LinearLayoutManager(context)
|
||||
else -> GridLayoutManager(context, columnCount)
|
||||
}
|
||||
adapter = ProfilePostsRecyclerViewAdapter(requireContext())
|
||||
adapter = ProfilePostsRecyclerViewAdapter()
|
||||
}
|
||||
}
|
||||
return view
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.h.pixeldroid.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.LayoutInflater
|
||||
|
@ -16,9 +15,7 @@ import com.h.pixeldroid.utils.ImageConverter.Companion.setSquareImageFromURL
|
|||
* [RecyclerView.Adapter] that can display a list of [PostMiniature]s and makes a call to the
|
||||
* specified [OnListFragmentInteractionListener].
|
||||
*/
|
||||
class ProfilePostsRecyclerViewAdapter(
|
||||
private val context: Context
|
||||
) : RecyclerView.Adapter<ProfilePostsRecyclerViewAdapter.ViewHolder>() {
|
||||
class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostsRecyclerViewAdapter.ViewHolder>() {
|
||||
private val posts: ArrayList<Status> = ArrayList()
|
||||
|
||||
fun addPosts(newPosts : List<Status>) {
|
||||
|
@ -37,9 +34,9 @@ class ProfilePostsRecyclerViewAdapter(
|
|||
val post = posts[position]
|
||||
setSquareImageFromURL(holder.postView, post.getPostPreviewURL(), holder.postPreview)
|
||||
holder.postPreview.setOnClickListener {
|
||||
val intent = Intent(context, PostActivity::class.java)
|
||||
val intent = Intent(holder.postPreview.context, PostActivity::class.java)
|
||||
intent.putExtra(Status.POST_TAG, post)
|
||||
context.startActivity(intent)
|
||||
holder.postPreview.context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,25 +4,44 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.PostActivity
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.SearchActivity
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.objects.DiscoverPost
|
||||
import com.h.pixeldroid.objects.DiscoverPosts
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
/**
|
||||
* This fragment lets you search and use PixelFed's Discover feature
|
||||
* This fragment lets you search and use Pixelfed's Discover feature
|
||||
*/
|
||||
|
||||
class SearchDiscoverFragment : Fragment() {
|
||||
lateinit var api: PixelfedAPI
|
||||
private lateinit var api: PixelfedAPI
|
||||
private lateinit var preferences: SharedPreferences
|
||||
private lateinit var recycler : RecyclerView
|
||||
private lateinit var adapter : DiscoverRecyclerViewAdapter
|
||||
private lateinit var accessToken: String
|
||||
private lateinit var discoverProgressBar: ProgressBar
|
||||
private lateinit var discoverRefreshLayout: SwipeRefreshLayout
|
||||
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -37,6 +56,11 @@ class SearchDiscoverFragment : Fragment() {
|
|||
intent.putExtra("searchFeed", search.text.toString())
|
||||
startActivity(intent)
|
||||
}
|
||||
// Set posts RecyclerView as a grid with 3 columns
|
||||
recycler = view.findViewById(R.id.discoverList)
|
||||
recycler.layoutManager = GridLayoutManager(requireContext(), 3)
|
||||
adapter = DiscoverRecyclerViewAdapter()
|
||||
recycler.adapter = adapter
|
||||
|
||||
return view
|
||||
}
|
||||
|
@ -49,5 +73,67 @@ class SearchDiscoverFragment : Fragment() {
|
|||
api = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
accessToken = preferences.getString("accessToken", "") ?: ""
|
||||
|
||||
discoverProgressBar = view.findViewById(R.id.discoverProgressBar)
|
||||
discoverRefreshLayout = view.findViewById(R.id.discoverRefreshLayout)
|
||||
|
||||
getDiscover()
|
||||
|
||||
discoverRefreshLayout.setOnRefreshListener {
|
||||
getDiscover()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDiscover() {
|
||||
|
||||
api.discover("Bearer $accessToken")
|
||||
.enqueue(object : Callback<DiscoverPosts> {
|
||||
|
||||
override fun onFailure(call: Call<DiscoverPosts>, t: Throwable) {
|
||||
Log.e("SearchDiscoverFragment:", t.toString())
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call<DiscoverPosts>, response: Response<DiscoverPosts>) {
|
||||
if(response.code() == 200) {
|
||||
val discoverPosts = response.body()!!
|
||||
adapter.addPosts(discoverPosts.posts)
|
||||
discoverProgressBar.visibility = View.GONE
|
||||
discoverRefreshLayout.isRefreshing = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* [RecyclerView.Adapter] that can display a list of [DiscoverPost]s
|
||||
*/
|
||||
class DiscoverRecyclerViewAdapter: RecyclerView.Adapter<DiscoverRecyclerViewAdapter.ViewHolder>() {
|
||||
private val posts: ArrayList<DiscoverPost> = ArrayList()
|
||||
|
||||
fun addPosts(newPosts : List<DiscoverPost>) {
|
||||
posts.clear()
|
||||
posts.addAll(newPosts)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.fragment_profile_posts, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val post = posts[position]
|
||||
ImageConverter.setSquareImageFromURL(holder.postView, post.thumb, holder.postPreview)
|
||||
holder.postPreview.setOnClickListener {
|
||||
val intent = Intent(holder.postView.context, PostActivity::class.java)
|
||||
intent.putExtra(Status.DISCOVER_TAG, post)
|
||||
holder.postView.context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = posts.size
|
||||
|
||||
inner class ViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
|
||||
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,10 @@ import androidx.recyclerview.widget.DiffUtil
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.bumptech.glide.ListPreloader.PreloadModelProvider
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.objects.FeedContent
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import kotlinx.android.synthetic.main.fragment_feed.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package com.h.pixeldroid.objects
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
/*
|
||||
NOT DOCUMENTED, USE WITH CAUTION
|
||||
*/
|
||||
|
||||
data class DiscoverPost(
|
||||
val type: String?, //This is probably an enum, with these values: https://github.com/pixelfed/pixelfed/blob/700c7805cecc364b68b9cfe20df00608e0f6c465/app/Status.php#L31
|
||||
val url: String?, //URL to post
|
||||
val thumb: String? //URL to thumbnail
|
||||
) : Serializable
|
|
@ -0,0 +1,8 @@
|
|||
package com.h.pixeldroid.objects
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class DiscoverPosts(
|
||||
//Required attributes
|
||||
val posts: List<DiscoverPost>
|
||||
) : Serializable
|
|
@ -94,6 +94,7 @@ data class Status(
|
|||
const val POST_TAG = "postTag"
|
||||
const val POST_FRAG_TAG = "postFragTag"
|
||||
const val DOMAIN_TAG = "domainTag"
|
||||
const val DISCOVER_TAG = "discoverTag"
|
||||
}
|
||||
|
||||
fun getPostUrl() : String? = media_attachments?.getOrNull(0)?.url
|
||||
|
|
|
@ -6,12 +6,26 @@
|
|||
android:layout_height="match_parent"
|
||||
tools:context=".PostActivity">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/postFragmentSingle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
<ProgressBar
|
||||
android:id="@+id/postProgressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/postFragmentSingle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" >
|
||||
|
||||
</androidx.fragment.app.FragmentContainerView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -16,7 +16,7 @@
|
|||
app:errorEnabled="true"
|
||||
app:layout_constraintEnd_toStartOf="@+id/searchButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" >
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/searchEditText"
|
||||
|
@ -28,17 +28,6 @@
|
|||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/searchProgressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/search" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/searchButton"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -49,4 +38,35 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/search" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/discoverProgressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/search" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/discoverRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/search">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/discoverList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
app:layoutManager="LinearLayoutManager"
|
||||
app:layout_constraintTop_toBottomOf="@+id/search" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue