Search (#121)
* Search * refactor * add hashtags * clean up xml, add some spacing to hashtag list * Fix invalidation causing failures * Move Tag's id to read-only member value * Refactor * Add test, rename things to be more sensible
This commit is contained in:
parent
92c534ca1b
commit
ce0b914d78
@ -106,7 +106,7 @@ class LoginCheckIntent {
|
||||
|
||||
onView(withId(R.id.whatsAnInstanceTextView)).perform(scrollTo()).perform(click())
|
||||
|
||||
Thread.sleep(10000)
|
||||
Thread.sleep(3000)
|
||||
|
||||
intended(expectedIntent)
|
||||
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
|
||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||
@ -48,6 +49,52 @@ class MockedServerTest {
|
||||
activityScenario = ActivityScenario.launch(MainActivity::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchPosts() {
|
||||
activityScenario.onActivity{
|
||||
a -> a.findViewById<TabLayout>(R.id.tabs).getTabAt(1)?.select()
|
||||
}
|
||||
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.searchEditText)).perform(ViewActions.replaceText("caturday"), ViewActions.closeSoftKeyboard())
|
||||
|
||||
onView(withId(R.id.searchButton)).perform(click())
|
||||
Thread.sleep(3000)
|
||||
onView(first(withId(R.id.username))).check(matches(withText("Memo")))
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchHashtags() {
|
||||
activityScenario.onActivity{
|
||||
a -> a.findViewById<TabLayout>(R.id.tabs).getTabAt(1)?.select()
|
||||
}
|
||||
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.searchEditText)).perform(ViewActions.replaceText("#caturday"), ViewActions.closeSoftKeyboard())
|
||||
|
||||
onView(withId(R.id.searchButton)).perform(click())
|
||||
Thread.sleep(3000)
|
||||
onView(first(withId(R.id.tag_name))).check(matches(withText("#caturday")))
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchAccounts() {
|
||||
activityScenario.onActivity{
|
||||
a -> a.findViewById<TabLayout>(R.id.tabs).getTabAt(1)?.select()
|
||||
}
|
||||
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.searchEditText)).perform(ViewActions.replaceText("@dansup"), ViewActions.closeSoftKeyboard())
|
||||
|
||||
onView(withId(R.id.searchButton)).perform(click())
|
||||
Thread.sleep(3000)
|
||||
onView(first(withId(R.id.account_entry_username))).check(matches(withText("dansup")))
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testFollowersTextView() {
|
||||
activityScenario.onActivity{
|
||||
@ -58,7 +105,7 @@ 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)
|
||||
|
File diff suppressed because one or more lines are too long
@ -35,7 +35,6 @@
|
||||
android:theme="@style/AppTheme.Launcher">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
@ -54,6 +53,17 @@
|
||||
android:scheme="@string/auth_scheme" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SearchActivity"
|
||||
android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.h.pixeldroid.fileprovider"
|
||||
|
@ -5,7 +5,7 @@ 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.fragments.feeds.AccountListFragment
|
||||
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
|
||||
@ -14,7 +14,7 @@ import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
class FollowsActivity : AppCompatActivity() {
|
||||
var followsFragment = FollowsFragment()
|
||||
var followsFragment = AccountListFragment()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -16,17 +16,20 @@ import com.google.android.material.navigation.NavigationView
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.h.pixeldroid.fragments.NewPostFragment
|
||||
import com.h.pixeldroid.fragments.feeds.HomeFragment
|
||||
import com.h.pixeldroid.fragments.ProfileFragment
|
||||
import com.h.pixeldroid.fragments.SearchDiscoverFragment
|
||||
import com.h.pixeldroid.fragments.feeds.PostsFeedFragment
|
||||
import com.h.pixeldroid.fragments.feeds.NotificationsFragment
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
private lateinit var drawerLayout: DrawerLayout
|
||||
private lateinit var viewPager: ViewPager2
|
||||
private lateinit var tabLayout: TabLayout
|
||||
private lateinit var preferences: SharedPreferences
|
||||
private val searchDiscoverFragment: SearchDiscoverFragment = SearchDiscoverFragment()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(R.style.AppTheme_NoActionBar)
|
||||
@ -47,8 +50,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
navigationView.setNavigationItemSelectedListener(this)
|
||||
|
||||
val tabs = arrayOf(
|
||||
HomeFragment(),
|
||||
Fragment(),
|
||||
PostsFeedFragment(),
|
||||
searchDiscoverFragment,
|
||||
NewPostFragment(),
|
||||
NotificationsFragment(),
|
||||
ProfileFragment()
|
||||
|
@ -15,7 +15,7 @@ class PostActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_post)
|
||||
|
||||
val status = intent.getSerializableExtra(POST_TAG) as Status
|
||||
val status = intent.getSerializableExtra(POST_TAG) as Status?
|
||||
|
||||
postFragment = PostFragment()
|
||||
val arguments = Bundle()
|
||||
|
104
app/src/main/java/com/h/pixeldroid/SearchActivity.kt
Normal file
104
app/src/main/java/com/h/pixeldroid/SearchActivity.kt
Normal file
@ -0,0 +1,104 @@
|
||||
package com.h.pixeldroid
|
||||
|
||||
import android.app.SearchManager
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.h.pixeldroid.fragments.feeds.search.SearchAccountFragment
|
||||
import com.h.pixeldroid.fragments.feeds.search.SearchHashtagFragment
|
||||
import com.h.pixeldroid.fragments.feeds.search.SearchPostsFragment
|
||||
import com.h.pixeldroid.objects.Results
|
||||
|
||||
class SearchActivity : AppCompatActivity() {
|
||||
private lateinit var preferences: SharedPreferences
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_search)
|
||||
|
||||
var query = intent.getSerializableExtra("searchFeed") as String
|
||||
query = query.trim()
|
||||
|
||||
val searchType = if (query.startsWith("#")){
|
||||
Results.SearchType.hashtags
|
||||
} else if(query.startsWith("@")){
|
||||
Results.SearchType.accounts
|
||||
} else Results.SearchType.statuses
|
||||
|
||||
if(searchType != Results.SearchType.statuses) query = query.drop(1)
|
||||
|
||||
val tabs = createSearchTabs(query)
|
||||
|
||||
setupTabs(tabs, searchType)
|
||||
}
|
||||
|
||||
private fun createSearchTabs(query: String): Array<Fragment>{
|
||||
|
||||
val searchFeedFragment =
|
||||
SearchPostsFragment()
|
||||
val searchAccountListFragment =
|
||||
SearchAccountFragment()
|
||||
val searchHashtagFragment: Fragment = SearchHashtagFragment()
|
||||
val arguments = Bundle()
|
||||
arguments.putSerializable("searchFeed", query)
|
||||
searchFeedFragment.arguments = arguments
|
||||
searchAccountListFragment.arguments = arguments
|
||||
searchHashtagFragment.arguments = arguments
|
||||
return arrayOf(
|
||||
searchFeedFragment,
|
||||
searchAccountListFragment,
|
||||
searchHashtagFragment
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupTabs(
|
||||
tabs: Array<Fragment>,
|
||||
searchType: Results.SearchType
|
||||
){
|
||||
val viewPager = findViewById<ViewPager2>(R.id.search_view_pager)
|
||||
viewPager.adapter = object : FragmentStateAdapter(this) {
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return tabs[position]
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 3
|
||||
}
|
||||
}
|
||||
val tabLayout = findViewById<TabLayout>(R.id.search_tabs)
|
||||
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
|
||||
when(position){
|
||||
0 -> tab.text = "POSTS"
|
||||
1 -> tab.text = "ACCOUNTS"
|
||||
2 -> tab.text = "HASHTAGS"
|
||||
}
|
||||
}.attach()
|
||||
when(searchType){
|
||||
Results.SearchType.statuses -> tabLayout.selectTab(tabLayout.getTabAt(0))
|
||||
Results.SearchType.accounts -> tabLayout.selectTab(tabLayout.getTabAt(1))
|
||||
Results.SearchType.hashtags -> tabLayout.selectTab(tabLayout.getTabAt(2))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
if (intent.action == Intent.ACTION_SEARCH) {
|
||||
intent.getStringExtra(SearchManager.QUERY)?.also { query ->
|
||||
search(query)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun search(query: String){
|
||||
Log.e("search", "")
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +135,22 @@ interface PixelfedAPI {
|
||||
@Query("local") local: Boolean? = null
|
||||
): Call<List<Status>>
|
||||
|
||||
@GET("/api/v2/search")
|
||||
fun search(
|
||||
//The authorization header needs to be of the form "Bearer <token>"
|
||||
@Header("Authorization") authorization: String,
|
||||
@Query("account_id") account_id: String? = null,
|
||||
@Query("max_id") max_id: String? = null,
|
||||
@Query("min_id") min_id: String? = null,
|
||||
@Query("type") type: Results.SearchType? = null,
|
||||
@Query("exclude_unreviewed") exclude_unreviewed: Boolean? = null,
|
||||
@Query("q") q: String,
|
||||
@Query("resolve") resolve: Boolean? = null,
|
||||
@Query("limit") limit: String? = null,
|
||||
@Query("offset") offset: Int? = null,
|
||||
@Query("following") following: Boolean? = null
|
||||
): Call<Results>
|
||||
|
||||
/*
|
||||
Note: as of 0.10.8, Pixelfed does not seem to respect the Mastodon API documentation,
|
||||
you *need* to pass one of the so-called "optional" arguments. See:
|
||||
|
@ -0,0 +1,53 @@
|
||||
package com.h.pixeldroid.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.SearchActivity
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
|
||||
/**
|
||||
* This fragment lets you search and use PixelFed's Discover feature
|
||||
*/
|
||||
|
||||
class SearchDiscoverFragment : Fragment() {
|
||||
lateinit var api: PixelfedAPI
|
||||
private lateinit var preferences: SharedPreferences
|
||||
private lateinit var accessToken: String
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_search, container, false)
|
||||
val button = view.findViewById<Button>(R.id.searchButton)
|
||||
val search = view.findViewById<EditText>(R.id.searchEditText)
|
||||
button.setOnClickListener {
|
||||
val intent = Intent(context, SearchActivity::class.java)
|
||||
intent.putExtra("searchFeed", search.text.toString())
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
preferences = requireActivity().getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
api = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
accessToken = preferences.getString("accessToken", "") ?: ""
|
||||
|
||||
}
|
||||
}
|
@ -22,10 +22,10 @@ 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 kotlinx.android.synthetic.main.account_list_entry.view.*
|
||||
import retrofit2.Call
|
||||
|
||||
class FollowsFragment : FeedFragment<Account, FollowsFragment.FollowsRecyclerViewAdapter.ViewHolder>() {
|
||||
open class AccountListFragment : FeedFragment<Account, AccountListFragment.FollowsRecyclerViewAdapter.ViewHolder>() {
|
||||
lateinit var profilePicRequest: RequestBuilder<Drawable>
|
||||
|
||||
override fun onCreateView(
|
||||
@ -45,7 +45,7 @@ class FollowsFragment : FeedFragment<Account, FollowsFragment.FollowsRecyclerVie
|
||||
//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
|
||||
Glide.with(this), adapter as AccountListFragment.FollowsRecyclerViewAdapter, sizeProvider, 4
|
||||
)
|
||||
list.addOnScrollListener(preloader)
|
||||
|
||||
@ -54,9 +54,7 @@ class FollowsFragment : FeedFragment<Account, FollowsFragment.FollowsRecyclerVie
|
||||
|
||||
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 = makeContent()
|
||||
|
||||
content.observe(viewLifecycleOwner,
|
||||
Observer { c ->
|
||||
@ -66,35 +64,54 @@ class FollowsFragment : FeedFragment<Account, FollowsFragment.FollowsRecyclerVie
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
internal open fun makeContent(): LiveData<PagedList<Account>> {
|
||||
val id = arguments?.getSerializable(ACCOUNT_ID_TAG) as String
|
||||
val following = arguments?.getSerializable(FOLLOWING_TAG) as Boolean
|
||||
|
||||
val (makeInitialCall, makeAfterCall)
|
||||
= makeCalls(following, id)
|
||||
|
||||
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
|
||||
factory = FeedDataSourceFactory(::makeInitialCall, ::makeAfterCall)
|
||||
val dataSource = FeedDataSource(makeInitialCall, makeAfterCall)
|
||||
factory = FeedDataSourceFactory(dataSource)
|
||||
return LivePagedListBuilder(factory, config).build()
|
||||
}
|
||||
|
||||
inner class FollowsRecyclerViewAdapter : FeedsRecyclerViewAdapter<Account,FollowsRecyclerViewAdapter.ViewHolder>() {
|
||||
private fun makeCalls(following: Boolean, id: String):
|
||||
Pair<(Int) -> Call<List<Account>>, (Int, String) -> Call<List<Account>>> {
|
||||
val makeInitialCall: (Int) -> Call<List<Account>> =
|
||||
if (following) { requestedLoadSize ->
|
||||
pixelfedAPI.followers(
|
||||
id, "Bearer $accessToken",
|
||||
limit = requestedLoadSize
|
||||
)
|
||||
} else { requestedLoadSize ->
|
||||
pixelfedAPI.following(
|
||||
id, "Bearer $accessToken",
|
||||
limit = requestedLoadSize
|
||||
)
|
||||
}
|
||||
val makeAfterCall: (Int, String) -> Call<List<Account>> =
|
||||
if (following) { requestedLoadSize, key ->
|
||||
pixelfedAPI.followers(
|
||||
id, "Bearer $accessToken",
|
||||
since_id = key, limit = requestedLoadSize
|
||||
)
|
||||
} else { requestedLoadSize, key ->
|
||||
pixelfedAPI.following(
|
||||
id, "Bearer $accessToken",
|
||||
since_id = key, limit = requestedLoadSize
|
||||
)
|
||||
}
|
||||
return Pair(makeInitialCall, makeAfterCall)
|
||||
}
|
||||
|
||||
inner class FollowsRecyclerViewAdapter : FeedsRecyclerViewAdapter<Account,FollowsRecyclerViewAdapter.ViewHolder>(),
|
||||
ListPreloader.PreloadModelProvider<Account> {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.fragment_follows, parent, false)
|
||||
.inflate(R.layout.account_list_entry, parent, false)
|
||||
context = view.context
|
||||
return ViewHolder(view)
|
||||
}
|
||||
@ -109,8 +126,8 @@ class FollowsFragment : FeedFragment<Account, FollowsFragment.FollowsRecyclerVie
|
||||
}
|
||||
|
||||
inner class ViewHolder(val mView : View) : RecyclerView.ViewHolder(mView) {
|
||||
val avatar : ImageView = mView.follows_avatar
|
||||
val username : TextView = mView.follows_username
|
||||
val avatar : ImageView = mView.account_entry_avatar
|
||||
val username : TextView = mView.account_entry_username
|
||||
}
|
||||
|
||||
override fun getPreloadItems(position : Int) : MutableList<Account> {
|
@ -25,6 +25,7 @@ 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
|
||||
@ -33,7 +34,7 @@ import retrofit2.Response
|
||||
open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment() {
|
||||
|
||||
lateinit var content: LiveData<PagedList<T>>
|
||||
lateinit var factory: FeedDataSourceFactory
|
||||
lateinit var factory: FeedDataSourceFactory<FeedDataSource>
|
||||
|
||||
protected var accessToken: String? = null
|
||||
protected lateinit var pixelfedAPI: PixelfedAPI
|
||||
@ -42,7 +43,7 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
|
||||
protected lateinit var list : RecyclerView
|
||||
protected lateinit var adapter : FeedsRecyclerViewAdapter<T, VH>
|
||||
protected lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
||||
private lateinit var loadingIndicator: ProgressBar
|
||||
internal lateinit var loadingIndicator: ProgressBar
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
@ -75,11 +76,14 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
|
||||
|
||||
}
|
||||
|
||||
|
||||
inner class FeedDataSource(private val makeInitialCall: (Int) -> Call<List<T>>,
|
||||
private val makeAfterCall: (Int, String) -> Call<List<T>>
|
||||
open inner class FeedDataSource(private val makeInitialCall: ((Int) -> Call<List<T>>)?,
|
||||
private val makeAfterCall: ((Int, String) -> Call<List<T>>)?
|
||||
): ItemKeyedDataSource<String, T>() {
|
||||
|
||||
open fun newSource(): FeedDataSource {
|
||||
return FeedDataSource(makeInitialCall, makeAfterCall)
|
||||
}
|
||||
|
||||
//We use the id as the key
|
||||
override fun getKey(item: T): String {
|
||||
return item.id
|
||||
@ -89,13 +93,13 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
|
||||
params: LoadInitialParams<String>,
|
||||
callback: LoadInitialCallback<T>
|
||||
) {
|
||||
enqueueCall(makeInitialCall(params.requestedLoadSize), callback)
|
||||
enqueueCall(makeInitialCall!!(params.requestedLoadSize), callback)
|
||||
}
|
||||
|
||||
//This is called to when we get to the bottom of the loaded content, so we want statuses
|
||||
//older than the given key (params.key)
|
||||
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<T>) {
|
||||
enqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
|
||||
enqueueCall(makeAfterCall!!(params.requestedLoadSize, params.key), callback)
|
||||
}
|
||||
|
||||
override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<T>) {
|
||||
@ -124,16 +128,15 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
|
||||
})
|
||||
}
|
||||
}
|
||||
inner class FeedDataSourceFactory(
|
||||
private val makeInitialCall: (Int) -> Call<List<T>>,
|
||||
private val makeAfterCall: (Int, String) -> Call<List<T>>
|
||||
open inner class FeedDataSourceFactory<DS: FeedDataSource>(
|
||||
private val dataSource: DS
|
||||
): DataSource.Factory<String, T>() {
|
||||
lateinit var liveData: MutableLiveData<FeedDataSource>
|
||||
lateinit var liveData: MutableLiveData<DS>
|
||||
|
||||
override fun create(): DataSource<String, T> {
|
||||
val dataSource = FeedDataSource(::makeInitialCall.get(), ::makeAfterCall.get())
|
||||
val dataSource = dataSource.newSource()
|
||||
liveData = MutableLiveData()
|
||||
liveData.postValue(dataSource)
|
||||
liveData.postValue(dataSource as DS)
|
||||
return dataSource
|
||||
}
|
||||
|
||||
@ -151,7 +154,7 @@ abstract class FeedsRecyclerViewAdapter<T: FeedContent, VH : RecyclerView.ViewHo
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
), PreloadModelProvider<T> {
|
||||
){
|
||||
|
||||
protected lateinit var context: Context
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.N
|
||||
//Make Glide be aware of the recyclerview and pre-load images
|
||||
val sizeProvider: ListPreloader.PreloadSizeProvider<Notification> = ViewPreloadSizeProvider()
|
||||
val preloader: RecyclerViewPreloader<Notification> = RecyclerViewPreloader(
|
||||
Glide.with(this), adapter, sizeProvider, 4
|
||||
Glide.with(this), adapter as NotificationsFragment.NotificationsRecyclerViewAdapter, sizeProvider, 4
|
||||
)
|
||||
list.addOnScrollListener(preloader)
|
||||
|
||||
@ -89,14 +89,16 @@ class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.N
|
||||
}
|
||||
|
||||
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
|
||||
factory = FeedDataSourceFactory(::makeInitialCall, ::makeAfterCall)
|
||||
val dataSource = FeedDataSource(::makeInitialCall, ::makeAfterCall)
|
||||
factory = FeedDataSourceFactory(dataSource)
|
||||
return LivePagedListBuilder(factory, config).build()
|
||||
}
|
||||
|
||||
/**
|
||||
* [RecyclerView.Adapter] that can display a [Notification]
|
||||
*/
|
||||
inner class NotificationsRecyclerViewAdapter: FeedsRecyclerViewAdapter<Notification, NotificationsRecyclerViewAdapter.ViewHolder>() {
|
||||
inner class NotificationsRecyclerViewAdapter: FeedsRecyclerViewAdapter<Notification, NotificationsRecyclerViewAdapter.ViewHolder>(),
|
||||
ListPreloader.PreloadModelProvider<Notification> {
|
||||
|
||||
private val mOnClickListener: View.OnClickListener
|
||||
|
||||
|
@ -24,7 +24,7 @@ import com.h.pixeldroid.objects.Status
|
||||
import retrofit2.Call
|
||||
|
||||
|
||||
class HomeFragment : FeedFragment<Status, PostViewHolder>() {
|
||||
open class PostsFeedFragment : FeedFragment<Status, PostViewHolder>() {
|
||||
|
||||
lateinit var picRequest: RequestBuilder<Drawable>
|
||||
|
||||
@ -39,14 +39,14 @@ class HomeFragment : FeedFragment<Status, PostViewHolder>() {
|
||||
.asDrawable().fitCenter()
|
||||
.placeholder(ColorDrawable(Color.GRAY))
|
||||
|
||||
adapter = HomeRecyclerViewAdapter(this)
|
||||
adapter = PostsFeedRecyclerViewAdapter(this)
|
||||
list.adapter = adapter
|
||||
|
||||
|
||||
//Make Glide be aware of the recyclerview and pre-load images
|
||||
val sizeProvider: ListPreloader.PreloadSizeProvider<Status> = ViewPreloadSizeProvider()
|
||||
val preloader: RecyclerViewPreloader<Status> = RecyclerViewPreloader(
|
||||
Glide.with(this), adapter, sizeProvider, 4
|
||||
Glide.with(this), adapter as PostsFeedFragment.PostsFeedRecyclerViewAdapter, sizeProvider, 4
|
||||
)
|
||||
list.addOnScrollListener(preloader)
|
||||
|
||||
@ -64,7 +64,7 @@ class HomeFragment : FeedFragment<Status, PostViewHolder>() {
|
||||
})
|
||||
}
|
||||
|
||||
private fun makeContent(): LiveData<PagedList<Status>> {
|
||||
internal open fun makeContent(): LiveData<PagedList<Status>> {
|
||||
fun makeInitialCall(requestedLoadSize: Int): Call<List<Status>> {
|
||||
return pixelfedAPI
|
||||
.timelineHome("Bearer $accessToken", limit="$requestedLoadSize")
|
||||
@ -75,15 +75,17 @@ class HomeFragment : FeedFragment<Status, PostViewHolder>() {
|
||||
limit="$requestedLoadSize")
|
||||
}
|
||||
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
|
||||
factory = FeedDataSourceFactory(::makeInitialCall, ::makeAfterCall)
|
||||
val dataSource = FeedDataSource(::makeInitialCall, ::makeAfterCall)
|
||||
factory = FeedDataSourceFactory(dataSource)
|
||||
return LivePagedListBuilder(factory, config).build()
|
||||
}
|
||||
|
||||
/**
|
||||
* [RecyclerView.Adapter] that can display a list of Statuses
|
||||
*/
|
||||
inner class HomeRecyclerViewAdapter(private val homeFragment: HomeFragment)
|
||||
: FeedsRecyclerViewAdapter<Status, PostViewHolder>() {
|
||||
inner class PostsFeedRecyclerViewAdapter(private val postsFeedFragment: PostsFeedFragment)
|
||||
: FeedsRecyclerViewAdapter<Status, PostViewHolder>(),
|
||||
ListPreloader.PreloadModelProvider<Status> {
|
||||
private val api = pixelfedAPI
|
||||
private val credential = "Bearer $accessToken"
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
|
||||
@ -104,7 +106,7 @@ class HomeFragment : FeedFragment<Status, PostViewHolder>() {
|
||||
holder.postPic.maxHeight = metrics.heightPixels
|
||||
|
||||
//Setup the post layout
|
||||
post.setupPost(holder.postView, picRequest, homeFragment)
|
||||
post.setupPost(holder.postView, picRequest, postsFeedFragment)
|
||||
|
||||
//Set the special HTML text
|
||||
post.setDescription(holder.postView, api, credential)
|
@ -0,0 +1,100 @@
|
||||
package com.h.pixeldroid.fragments.feeds.search
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.LivePagedListBuilder
|
||||
import androidx.paging.PagedList
|
||||
import com.h.pixeldroid.fragments.feeds.AccountListFragment
|
||||
import com.h.pixeldroid.fragments.feeds.FeedFragment
|
||||
import com.h.pixeldroid.objects.Account
|
||||
import com.h.pixeldroid.objects.Results
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
class SearchAccountFragment: AccountListFragment(){
|
||||
|
||||
private lateinit var query: String
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
query = arguments?.getSerializable("searchFeed") as String
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
inner class SearchAccountListDataSource: FeedDataSource(null, null){
|
||||
|
||||
override fun newSource(): FeedDataSource {
|
||||
return SearchAccountListDataSource()
|
||||
}
|
||||
|
||||
private fun makeInitialCall(requestedLoadSize: Int): Call<Results> {
|
||||
return pixelfedAPI
|
||||
.search("Bearer $accessToken",
|
||||
limit="$requestedLoadSize", q=query,
|
||||
type = Results.SearchType.accounts)
|
||||
}
|
||||
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
|
||||
return pixelfedAPI
|
||||
.search("Bearer $accessToken", max_id=key,
|
||||
limit="$requestedLoadSize", q = query,
|
||||
type = Results.SearchType.accounts)
|
||||
}
|
||||
override fun loadInitial(
|
||||
params: LoadInitialParams<String>,
|
||||
callback: LoadInitialCallback<Account>
|
||||
) {
|
||||
enqueueCall(makeInitialCall(params.requestedLoadSize), callback)
|
||||
}
|
||||
|
||||
//This is called to when we get to the bottom of the loaded content, so we want statuses
|
||||
//older than the given key (params.key)
|
||||
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Account>) {
|
||||
enqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
|
||||
}
|
||||
private fun enqueueCall(call: Call<Results>, callback: LoadCallback<Account>){
|
||||
|
||||
call.enqueue(object : Callback<Results> {
|
||||
override fun onResponse(call: Call<Results>, response: Response<Results>) {
|
||||
if (response.code() == 200) {
|
||||
val notifications = response.body()!!.accounts as ArrayList<Account>
|
||||
callback.onResult(notifications as List<Account>)
|
||||
|
||||
} else{
|
||||
Toast.makeText(context,"Something went wrong while loading", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Results>, t: Throwable) {
|
||||
Toast.makeText(context,"Could not get feed", Toast.LENGTH_SHORT).show()
|
||||
Log.e("FeedFragment", t.toString())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun makeContent(): LiveData<PagedList<Account>> {
|
||||
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
|
||||
factory =
|
||||
FeedFragment<Account, FollowsRecyclerViewAdapter.ViewHolder>()
|
||||
.FeedDataSourceFactory(
|
||||
SearchAccountListDataSource()
|
||||
)
|
||||
return LivePagedListBuilder(factory, config).build()
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
package com.h.pixeldroid.fragments.feeds.search
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
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.lifecycle.Observer
|
||||
import androidx.paging.LivePagedListBuilder
|
||||
import androidx.paging.PagedList
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
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.util.ViewPreloadSizeProvider
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.fragments.feeds.AccountListFragment
|
||||
import com.h.pixeldroid.fragments.feeds.FeedFragment
|
||||
import com.h.pixeldroid.fragments.feeds.FeedsRecyclerViewAdapter
|
||||
import com.h.pixeldroid.fragments.feeds.NotificationsFragment
|
||||
import com.h.pixeldroid.objects.Account
|
||||
import com.h.pixeldroid.objects.Notification
|
||||
import com.h.pixeldroid.objects.Results
|
||||
import com.h.pixeldroid.objects.Tag
|
||||
import kotlinx.android.synthetic.main.account_list_entry.view.*
|
||||
import kotlinx.android.synthetic.main.fragment_tags.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
class SearchHashtagFragment: FeedFragment<Tag, SearchHashtagFragment.TagsRecyclerViewAdapter.ViewHolder>(){
|
||||
|
||||
private lateinit var query: String
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
query = arguments?.getSerializable("searchFeed") as String
|
||||
|
||||
adapter = TagsRecyclerViewAdapter()
|
||||
list.adapter = adapter
|
||||
|
||||
return view
|
||||
}
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
content = makeContent()
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
inner class SearchTagsListDataSource: FeedDataSource(null, null){
|
||||
|
||||
override fun newSource(): FeedDataSource {
|
||||
return SearchTagsListDataSource()
|
||||
}
|
||||
|
||||
private fun makeInitialCall(requestedLoadSize: Int): Call<Results> {
|
||||
return pixelfedAPI
|
||||
.search("Bearer $accessToken",
|
||||
limit="$requestedLoadSize", q=query,
|
||||
type = Results.SearchType.hashtags)
|
||||
}
|
||||
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
|
||||
return pixelfedAPI
|
||||
.search("Bearer $accessToken", offset=key.toInt(),
|
||||
limit="$requestedLoadSize", q = query,
|
||||
type = Results.SearchType.hashtags)
|
||||
}
|
||||
|
||||
override fun getKey(item: Tag): String {
|
||||
val value = content.value
|
||||
val count = value?.loadedCount ?: 0
|
||||
return count.toString()
|
||||
}
|
||||
override fun loadInitial(
|
||||
params: LoadInitialParams<String>,
|
||||
callback: LoadInitialCallback<Tag>
|
||||
) {
|
||||
enqueueCall(makeInitialCall(params.requestedLoadSize), callback)
|
||||
}
|
||||
|
||||
//This is called to when we get to the bottom of the loaded content, so we want statuses
|
||||
//older than the given key (params.key)
|
||||
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Tag>) {
|
||||
enqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
|
||||
}
|
||||
|
||||
private fun enqueueCall(call: Call<Results>, callback: LoadCallback<Tag>){
|
||||
|
||||
call.enqueue(object : Callback<Results> {
|
||||
override fun onResponse(call: Call<Results>, response: Response<Results>) {
|
||||
if (response.code() == 200) {
|
||||
val notifications = response.body()!!.hashtags as ArrayList<Tag>
|
||||
callback.onResult(notifications as List<Tag>)
|
||||
|
||||
} else{
|
||||
Toast.makeText(context,"Something went wrong while loading", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Results>, t: Throwable) {
|
||||
Toast.makeText(context,"Could not get feed", Toast.LENGTH_SHORT).show()
|
||||
Log.e("FeedFragment", t.toString())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeContent(): LiveData<PagedList<Tag>> {
|
||||
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
|
||||
factory =
|
||||
FeedFragment<Tag, TagsRecyclerViewAdapter.ViewHolder>()
|
||||
.FeedDataSourceFactory(
|
||||
SearchTagsListDataSource()
|
||||
)
|
||||
return LivePagedListBuilder(factory, config).build()
|
||||
}
|
||||
|
||||
inner class TagsRecyclerViewAdapter : FeedsRecyclerViewAdapter<Tag, TagsRecyclerViewAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.fragment_tags, parent, false)
|
||||
context = view.context
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder : ViewHolder, position : Int) {
|
||||
val tag = getItem(position) ?: return
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
holder.name.text = "#" + tag.name
|
||||
|
||||
holder.mView.setOnClickListener { Log.e("Tag: ", tag.name) }
|
||||
}
|
||||
|
||||
inner class ViewHolder(val mView : View) : RecyclerView.ViewHolder(mView) {
|
||||
val name : TextView = mView.tag_name
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package com.h.pixeldroid.fragments.feeds.search
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.LivePagedListBuilder
|
||||
import androidx.paging.PagedList
|
||||
import com.h.pixeldroid.fragments.feeds.FeedFragment
|
||||
import com.h.pixeldroid.fragments.feeds.PostViewHolder
|
||||
import com.h.pixeldroid.fragments.feeds.PostsFeedFragment
|
||||
import com.h.pixeldroid.objects.Results
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
class SearchPostsFragment: PostsFeedFragment(){
|
||||
|
||||
private lateinit var query: String
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
query = arguments?.getSerializable("searchFeed") as String
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
inner class SearchFeedDataSource(
|
||||
) : FeedDataSource(null, null){
|
||||
|
||||
override fun newSource(): FeedDataSource {
|
||||
return SearchFeedDataSource()
|
||||
}
|
||||
|
||||
private fun makeInitialCall(requestedLoadSize: Int): Call<Results> {
|
||||
return pixelfedAPI
|
||||
.search("Bearer $accessToken",
|
||||
limit="$requestedLoadSize", q=query,
|
||||
type = Results.SearchType.statuses)
|
||||
}
|
||||
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
|
||||
return pixelfedAPI
|
||||
.search("Bearer $accessToken", max_id=key,
|
||||
limit="$requestedLoadSize", q = query,
|
||||
type = Results.SearchType.statuses)
|
||||
}
|
||||
override fun loadInitial(
|
||||
params: LoadInitialParams<String>,
|
||||
callback: LoadInitialCallback<Status>
|
||||
) {
|
||||
enqueueCall(makeInitialCall(params.requestedLoadSize), callback)
|
||||
}
|
||||
|
||||
//This is called to when we get to the bottom of the loaded content, so we want statuses
|
||||
//older than the given key (params.key)
|
||||
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Status>) {
|
||||
enqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
|
||||
}
|
||||
|
||||
private fun enqueueCall(call: Call<Results>, callback: LoadCallback<Status>){
|
||||
|
||||
call.enqueue(object : Callback<Results> {
|
||||
override fun onResponse(call: Call<Results>, response: Response<Results>) {
|
||||
if (response.code() == 200) {
|
||||
val notifications = response.body()!!.statuses as ArrayList<Status>
|
||||
callback.onResult(notifications as List<Status>)
|
||||
|
||||
} else{
|
||||
Log.e("FeedFragment", "got response code ${response.code()}")
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Results>, t: Throwable) {
|
||||
Log.e("FeedFragment", t.toString())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun makeContent(): LiveData<PagedList<Status>> {
|
||||
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
|
||||
factory = FeedFragment<Status, PostViewHolder>()
|
||||
.FeedDataSourceFactory(SearchFeedDataSource())
|
||||
return LivePagedListBuilder(factory, config).build()
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ data class Account(
|
||||
val acct: String,
|
||||
val url: String, //HTTPS URL
|
||||
//Display attributes
|
||||
val display_name: String,
|
||||
val display_name: String?,
|
||||
val note: String, //HTML
|
||||
val avatar: String, //URL
|
||||
val avatar_static: String, //URL
|
||||
@ -57,7 +57,7 @@ data class Account(
|
||||
const val FOLLOWING_TAG = "FollowingTag"
|
||||
|
||||
/**
|
||||
* @brief Opens an activity of the profile withn the given id
|
||||
* @brief Opens an activity of the profile with the given id
|
||||
*/
|
||||
fun getAccountFromId(id: String, api : PixelfedAPI, context: Context, credential: String) {
|
||||
Log.e("ACCOUNT_ID", id)
|
||||
|
14
app/src/main/java/com/h/pixeldroid/objects/Results.kt
Normal file
14
app/src/main/java/com/h/pixeldroid/objects/Results.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package com.h.pixeldroid.objects
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class Results(
|
||||
val accounts : List<Account>,
|
||||
val statuses : List<Status>,
|
||||
val hashtags: List<Tag>
|
||||
) : Serializable {
|
||||
|
||||
enum class SearchType: Serializable{
|
||||
accounts, hashtags, statuses
|
||||
}
|
||||
}
|
@ -100,7 +100,7 @@ data class Status(
|
||||
|
||||
fun getUsername() : CharSequence {
|
||||
var name = account.display_name
|
||||
if (name.isEmpty()) {
|
||||
if (name.isNullOrEmpty()) {
|
||||
name = account.username
|
||||
}
|
||||
return name
|
||||
|
@ -7,6 +7,10 @@ data class Tag(
|
||||
val name: String,
|
||||
val url: String,
|
||||
//Optional attributes
|
||||
val history: List<History>? = emptyList()
|
||||
) : Serializable
|
||||
val history: List<History>? = emptyList()) : Serializable, FeedContent() {
|
||||
//needed to be a FeedContent, this inheritance is a bit fickle. Do not use.
|
||||
override val id: String
|
||||
get() = "tag"
|
||||
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/follows_avatar"
|
||||
android:id="@+id/account_entry_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
@ -17,13 +17,13 @@
|
||||
tools:src="@drawable/ic_default_user" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/follows_username"
|
||||
android:id="@+id/account_entry_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_constraintStart_toEndOf="@+id/account_entry_avatar"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
31
app/src/main/res/layout/activity_search.xml
Normal file
31
app/src/main/res/layout/activity_search.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/search_tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabBackground="@color/colorPrimary"
|
||||
app:tabMode="fixed"/>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/search_view_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -4,7 +4,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context=".fragments.feeds.HomeFragment">
|
||||
tools:context=".fragments.feeds.PostsFeedFragment">
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
|
50
app/src/main/res/layout/fragment_search.xml
Normal file
50
app/src/main/res/layout/fragment_search.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?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:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/search"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:hint="Search"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintEnd_toStartOf="@+id/searchButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" >
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/searchEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textUri" />
|
||||
|
||||
</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"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Search"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/search"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/search" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
17
app/src/main/res/layout/fragment_tags.xml
Normal file
17
app/src/main/res/layout/fragment_tags.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?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">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tag_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="28dp"
|
||||
android:text="TextView"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
5
app/src/main/res/xml/searchable.xml
Normal file
5
app/src/main/res/xml/searchable.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:label="@string/app_name"
|
||||
android:hint="Search" >
|
||||
</searchable>
|
Loading…
x
Reference in New Issue
Block a user