migrating to ViewBinding part 4: Fragments (#2108)
* migrating to ViewBinding part 4: Fragment * fix imports * don't use viewBinding extension in ViewImage and ViewVideoFragment * don't use viewBinding extension in ViewImage and ViewVideoFragment
This commit is contained in:
parent
fbb0b11d83
commit
bea5098cc1
18 changed files with 412 additions and 349 deletions
|
@ -125,6 +125,7 @@ dependencies {
|
|||
implementation "androidx.emoji:emoji-appcompat:1.1.0"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
|
||||
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycleVersion"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
|
||||
|
|
|
@ -23,11 +23,13 @@ import android.view.ViewGroup
|
|||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.keylesspalace.tusky.databinding.FragmentAccountsInListBinding
|
||||
import com.keylesspalace.tusky.databinding.ItemFollowRequestBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
|
@ -38,9 +40,6 @@ import com.keylesspalace.tusky.viewmodel.State
|
|||
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
|
||||
import com.uber.autodispose.autoDispose
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.fragment_accounts_in_list.*
|
||||
import kotlinx.android.synthetic.main.item_follow_request.*
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -48,23 +47,11 @@ private typealias AccountInfo = Pair<Account, Boolean>
|
|||
|
||||
class AccountsInListFragment : DialogFragment(), Injectable {
|
||||
|
||||
companion object {
|
||||
private const val LIST_ID_ARG = "listId"
|
||||
private const val LIST_NAME_ARG = "listName"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(listId: String, listName: String): AccountsInListFragment {
|
||||
val args = Bundle().apply {
|
||||
putString(LIST_ID_ARG, listId)
|
||||
putString(LIST_NAME_ARG, listName)
|
||||
}
|
||||
return AccountsInListFragment().apply { arguments = args }
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
lateinit var viewModel: AccountsInListViewModel
|
||||
|
||||
private val viewModel: AccountsInListViewModel by viewModels { viewModelFactory }
|
||||
private val binding by viewBinding(FragmentAccountsInListBinding::bind)
|
||||
|
||||
private lateinit var listId: String
|
||||
private lateinit var listName: String
|
||||
|
@ -79,7 +66,6 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NORMAL, R.style.TuskyDialogFragmentStyle)
|
||||
viewModel = viewModelFactory.create(AccountsInListViewModel::class.java)
|
||||
val args = requireArguments()
|
||||
listId = args.getString(LIST_ID_ARG)!!
|
||||
listName = args.getString(LIST_NAME_ARG)!!
|
||||
|
@ -100,12 +86,11 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
accountsRecycler.layoutManager = LinearLayoutManager(view.context)
|
||||
accountsRecycler.adapter = adapter
|
||||
binding.accountsRecycler.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.accountsRecycler.adapter = adapter
|
||||
|
||||
accountsSearchRecycler.layoutManager = LinearLayoutManager(view.context)
|
||||
accountsSearchRecycler.adapter = searchAdapter
|
||||
binding.accountsSearchRecycler.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.accountsSearchRecycler.adapter = searchAdapter
|
||||
|
||||
viewModel.state
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
@ -114,15 +99,15 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
adapter.submitList(state.accounts.asRightOrNull() ?: listOf())
|
||||
|
||||
when (state.accounts) {
|
||||
is Either.Right -> messageView.hide()
|
||||
is Either.Right -> binding.messageView.hide()
|
||||
is Either.Left -> handleError(state.accounts.value)
|
||||
}
|
||||
|
||||
setupSearchView(state)
|
||||
}
|
||||
|
||||
searchView.isSubmitButtonEnabled = true
|
||||
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
binding.searchView.isSubmitButtonEnabled = true
|
||||
binding.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
viewModel.search(query ?: "")
|
||||
return true
|
||||
|
@ -141,30 +126,30 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
private fun setupSearchView(state: State) {
|
||||
if (state.searchResult == null) {
|
||||
searchAdapter.submitList(listOf())
|
||||
accountsSearchRecycler.hide()
|
||||
accountsRecycler.show()
|
||||
binding.accountsSearchRecycler.hide()
|
||||
binding.accountsRecycler.show()
|
||||
} else {
|
||||
val listAccounts = state.accounts.asRightOrNull() ?: listOf()
|
||||
val newList = state.searchResult.map { acc ->
|
||||
acc to listAccounts.contains(acc)
|
||||
}
|
||||
searchAdapter.submitList(newList)
|
||||
accountsSearchRecycler.show()
|
||||
accountsRecycler.hide()
|
||||
binding.accountsSearchRecycler.show()
|
||||
binding.accountsRecycler.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleError(error: Throwable) {
|
||||
messageView.show()
|
||||
binding.messageView.show()
|
||||
val retryAction = { _: View ->
|
||||
messageView.hide()
|
||||
binding.messageView.hide()
|
||||
viewModel.load(listId)
|
||||
}
|
||||
if (error is IOException) {
|
||||
messageView.setup(R.drawable.elephant_offline,
|
||||
binding.messageView.setup(R.drawable.elephant_offline,
|
||||
R.string.error_network, retryAction)
|
||||
} else {
|
||||
messageView.setup(R.drawable.elephant_error,
|
||||
binding.messageView.setup(R.drawable.elephant_error,
|
||||
R.string.error_generic, retryAction)
|
||||
}
|
||||
}
|
||||
|
@ -187,39 +172,28 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
inner class Adapter : ListAdapter<Account, Adapter.ViewHolder>(AccountDiffer) {
|
||||
inner class Adapter : ListAdapter<Account, BindingHolder<ItemFollowRequestBinding>>(AccountDiffer) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_follow_request, parent, false)
|
||||
return ViewHolder(view)
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemFollowRequestBinding> {
|
||||
val binding = ItemFollowRequestBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
val holder = BindingHolder(binding)
|
||||
|
||||
binding.notificationTextView.hide()
|
||||
binding.acceptButton.hide()
|
||||
binding.rejectButton.setOnClickListener {
|
||||
onRemoveFromList(getItem(holder.adapterPosition).id)
|
||||
}
|
||||
binding.rejectButton.contentDescription =
|
||||
binding.root.context.getString(R.string.action_remove_from_list)
|
||||
|
||||
return holder
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(getItem(position))
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
|
||||
View.OnClickListener, LayoutContainer {
|
||||
|
||||
override val containerView = itemView
|
||||
|
||||
init {
|
||||
acceptButton.hide()
|
||||
rejectButton.setOnClickListener(this)
|
||||
rejectButton.contentDescription =
|
||||
itemView.context.getString(R.string.action_remove_from_list)
|
||||
}
|
||||
|
||||
fun bind(account: Account) {
|
||||
displayNameTextView.text = account.name.emojify(account.emojis, displayNameTextView, animateEmojis)
|
||||
usernameTextView.text = account.username
|
||||
loadAvatar(account.avatar, avatar, radius, animateAvatar)
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
onRemoveFromList(getItem(adapterPosition).id)
|
||||
}
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemFollowRequestBinding>, position: Int) {
|
||||
val account = getItem(position)
|
||||
holder.binding.displayNameTextView.text = account.name.emojify(account.emojis, holder.binding.displayNameTextView, animateEmojis)
|
||||
holder.binding.usernameTextView.text = account.username
|
||||
loadAvatar(account.avatar, holder.binding.avatar, radius, animateAvatar)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,34 +206,36 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
return oldItem.second == newItem.second
|
||||
&& oldItem.first.deepEquals(newItem.first)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inner class SearchAdapter : ListAdapter<AccountInfo, SearchAdapter.ViewHolder>(SearchDiffer) {
|
||||
inner class SearchAdapter : ListAdapter<AccountInfo, BindingHolder<ItemFollowRequestBinding>>(SearchDiffer) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_follow_request, parent, false)
|
||||
return ViewHolder(view)
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemFollowRequestBinding> {
|
||||
val binding = ItemFollowRequestBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
val holder = BindingHolder(binding)
|
||||
|
||||
binding.notificationTextView.hide()
|
||||
binding.acceptButton.hide()
|
||||
binding.rejectButton.setOnClickListener {
|
||||
val (account, inAList) = getItem(holder.adapterPosition)
|
||||
if (inAList) {
|
||||
onRemoveFromList(account.id)
|
||||
} else {
|
||||
onAddToList(account)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
return holder
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemFollowRequestBinding>, position: Int) {
|
||||
val (account, inAList) = getItem(position)
|
||||
holder.bind(account, inAList)
|
||||
|
||||
}
|
||||
holder.binding.displayNameTextView.text = account.name.emojify(account.emojis, holder.binding.displayNameTextView, animateEmojis)
|
||||
holder.binding.usernameTextView.text = account.username
|
||||
loadAvatar(account.avatar, holder.binding.avatar, radius, animateAvatar)
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
|
||||
View.OnClickListener, LayoutContainer {
|
||||
|
||||
override val containerView = itemView
|
||||
|
||||
fun bind(account: Account, inAList: Boolean) {
|
||||
displayNameTextView.text = account.name.emojify(account.emojis, displayNameTextView, animateEmojis)
|
||||
usernameTextView.text = account.username
|
||||
loadAvatar(account.avatar, avatar, radius, animateAvatar)
|
||||
|
||||
rejectButton.apply {
|
||||
holder.binding.rejectButton.apply {
|
||||
contentDescription = if (inAList) {
|
||||
setImageResource(R.drawable.ic_reject_24dp)
|
||||
getString(R.string.action_remove_from_list)
|
||||
|
@ -269,20 +245,19 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
acceptButton.hide()
|
||||
rejectButton.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
val (account, inAList) = getItem(adapterPosition)
|
||||
if (inAList) {
|
||||
onRemoveFromList(account.id)
|
||||
} else {
|
||||
onAddToList(account)
|
||||
}
|
||||
companion object {
|
||||
private const val LIST_ID_ARG = "listId"
|
||||
private const val LIST_NAME_ARG = "listName"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(listId: String, listName: String): AccountsInListFragment {
|
||||
val args = Bundle().apply {
|
||||
putString(LIST_ID_ARG, listId)
|
||||
putString(LIST_NAME_ARG, listName)
|
||||
}
|
||||
return AccountsInListFragment().apply { arguments = args }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,6 +69,9 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
|||
|
||||
private val binding by viewBinding(ActivityViewMediaBinding::inflate)
|
||||
|
||||
val toolbar: View
|
||||
get() = binding.toolbar
|
||||
|
||||
var isToolbarVisible = true
|
||||
private set
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator
|
|||
import com.keylesspalace.tusky.AccountActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.ViewTagActivity
|
||||
import com.keylesspalace.tusky.databinding.FragmentTimelineBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.fragment.SFragment
|
||||
|
@ -38,7 +39,7 @@ import com.keylesspalace.tusky.util.CardViewMode
|
|||
import com.keylesspalace.tusky.util.NetworkState
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import kotlinx.android.synthetic.main.fragment_timeline.*
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConversationsFragment : SFragment(), StatusActionListener, Injectable, ReselectableFragment {
|
||||
|
@ -48,6 +49,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
|
||||
private val viewModel: ConversationsViewModel by viewModels { viewModelFactory }
|
||||
|
||||
private val binding by viewBinding(FragmentTimelineBinding::bind)
|
||||
|
||||
private lateinit var adapter: ConversationAdapter
|
||||
|
||||
private var layoutManager: LinearLayoutManager? = null
|
||||
|
@ -73,14 +76,14 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
|
||||
adapter = ConversationAdapter(statusDisplayOptions, this, ::onTopLoaded, viewModel::retry)
|
||||
|
||||
recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||
binding.recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||
layoutManager = LinearLayoutManager(view.context)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
recyclerView.adapter = adapter
|
||||
(recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.adapter = adapter
|
||||
(binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
|
||||
progressBar.hide()
|
||||
statusView.hide()
|
||||
binding.progressBar.hide()
|
||||
binding.statusView.hide()
|
||||
|
||||
initSwipeToRefresh()
|
||||
|
||||
|
@ -97,16 +100,16 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
|
||||
private fun initSwipeToRefresh() {
|
||||
viewModel.refreshState.observe(viewLifecycleOwner) {
|
||||
swipeRefreshLayout.isRefreshing = it == NetworkState.LOADING
|
||||
binding.swipeRefreshLayout.isRefreshing = it == NetworkState.LOADING
|
||||
}
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
viewModel.refresh()
|
||||
}
|
||||
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
}
|
||||
|
||||
private fun onTopLoaded() {
|
||||
recyclerView.scrollToPosition(0)
|
||||
binding.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
|
||||
override fun onReblog(reblog: Boolean, position: Int) {
|
||||
|
@ -183,7 +186,7 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
private fun jumpToTop() {
|
||||
if (isAdded) {
|
||||
layoutManager?.scrollToPosition(0)
|
||||
recyclerView.stopScroll()
|
||||
binding.recyclerView.stopScroll()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,7 @@ package com.keylesspalace.tusky.components.instancemute.fragment
|
|||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
|
@ -14,16 +12,17 @@ import com.google.android.material.snackbar.Snackbar
|
|||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.components.instancemute.adapter.DomainMutesAdapter
|
||||
import com.keylesspalace.tusky.components.instancemute.interfaces.InstanceActionListener
|
||||
import com.keylesspalace.tusky.databinding.FragmentInstanceListBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.view.EndlessOnScrollListener
|
||||
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
|
||||
import com.uber.autodispose.autoDispose
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.fragment_instance_list.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
@ -31,9 +30,12 @@ import java.io.IOException
|
|||
import javax.inject.Inject
|
||||
|
||||
class InstanceListFragment: Fragment(R.layout.fragment_instance_list), Injectable, InstanceActionListener {
|
||||
|
||||
@Inject
|
||||
lateinit var api: MastodonApi
|
||||
|
||||
private val binding by viewBinding(FragmentInstanceListBinding::bind)
|
||||
|
||||
private var fetching = false
|
||||
private var bottomId: String? = null
|
||||
private var adapter = DomainMutesAdapter(this)
|
||||
|
@ -42,12 +44,12 @@ class InstanceListFragment: Fragment(R.layout.fragment_instance_list), Injectabl
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
recyclerView.setHasFixedSize(true)
|
||||
recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||
recyclerView.adapter = adapter
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
val layoutManager = LinearLayoutManager(view.context)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
|
||||
scrollListener = object : EndlessOnScrollListener(layoutManager) {
|
||||
override fun onLoadMore(totalItemsCount: Int, view: RecyclerView) {
|
||||
|
@ -57,7 +59,7 @@ class InstanceListFragment: Fragment(R.layout.fragment_instance_list), Injectabl
|
|||
}
|
||||
}
|
||||
|
||||
recyclerView.addOnScrollListener(scrollListener)
|
||||
binding.recyclerView.addOnScrollListener(scrollListener)
|
||||
fetchInstances()
|
||||
}
|
||||
|
||||
|
@ -85,7 +87,7 @@ class InstanceListFragment: Fragment(R.layout.fragment_instance_list), Injectabl
|
|||
override fun onResponse(call: Call<Any>, response: Response<Any>) {
|
||||
if (response.isSuccessful) {
|
||||
adapter.removeItem(position)
|
||||
Snackbar.make(recyclerView, getString(R.string.confirmation_domain_unmuted, instance), Snackbar.LENGTH_LONG)
|
||||
Snackbar.make(binding.recyclerView, getString(R.string.confirmation_domain_unmuted, instance), Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_undo) {
|
||||
mute(true, instance, position)
|
||||
}
|
||||
|
@ -103,10 +105,10 @@ class InstanceListFragment: Fragment(R.layout.fragment_instance_list), Injectabl
|
|||
return
|
||||
}
|
||||
fetching = true
|
||||
instanceProgressBar.show()
|
||||
binding.instanceProgressBar.show()
|
||||
|
||||
if (id != null) {
|
||||
recyclerView.post { adapter.bottomLoading = true }
|
||||
binding.recyclerView.post { adapter.bottomLoading = true }
|
||||
}
|
||||
|
||||
api.domainBlocks(id, bottomId)
|
||||
|
@ -116,7 +118,7 @@ class InstanceListFragment: Fragment(R.layout.fragment_instance_list), Injectabl
|
|||
val instances = response.body()
|
||||
|
||||
if (response.isSuccessful && instances != null) {
|
||||
onFetchInstancesSuccess(instances, response.headers().get("Link"))
|
||||
onFetchInstancesSuccess(instances, response.headers()["Link"])
|
||||
} else {
|
||||
onFetchInstancesFailure(Exception(response.message()))
|
||||
}
|
||||
|
@ -127,7 +129,7 @@ class InstanceListFragment: Fragment(R.layout.fragment_instance_list), Injectabl
|
|||
|
||||
private fun onFetchInstancesSuccess(instances: List<String>, linkHeader: String?) {
|
||||
adapter.bottomLoading = false
|
||||
instanceProgressBar.hide()
|
||||
binding.instanceProgressBar.hide()
|
||||
|
||||
val links = HttpHeaderLink.parse(linkHeader)
|
||||
val next = HttpHeaderLink.findByRelationType(links, "next")
|
||||
|
@ -137,32 +139,32 @@ class InstanceListFragment: Fragment(R.layout.fragment_instance_list), Injectabl
|
|||
fetching = false
|
||||
|
||||
if (adapter.itemCount == 0) {
|
||||
messageView.show()
|
||||
messageView.setup(
|
||||
binding.messageView.show()
|
||||
binding.messageView.setup(
|
||||
R.drawable.elephant_friend_empty,
|
||||
R.string.message_empty,
|
||||
null
|
||||
)
|
||||
} else {
|
||||
messageView.hide()
|
||||
binding.messageView.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFetchInstancesFailure(throwable: Throwable) {
|
||||
fetching = false
|
||||
instanceProgressBar.hide()
|
||||
binding.instanceProgressBar.hide()
|
||||
Log.e(TAG, "Fetch failure", throwable)
|
||||
|
||||
if (adapter.itemCount == 0) {
|
||||
messageView.show()
|
||||
binding.messageView.show()
|
||||
if (throwable is IOException) {
|
||||
messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
messageView.hide()
|
||||
binding.messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
binding.messageView.hide()
|
||||
this.fetchInstances(null)
|
||||
}
|
||||
} else {
|
||||
messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
messageView.hide()
|
||||
binding.messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
binding.messageView.hide()
|
||||
this.fetchInstances(null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,12 +22,13 @@ import androidx.fragment.app.activityViewModels
|
|||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.components.report.ReportViewModel
|
||||
import com.keylesspalace.tusky.components.report.Screen
|
||||
import com.keylesspalace.tusky.databinding.FragmentReportDoneBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.util.Loading
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import kotlinx.android.synthetic.main.fragment_report_done.*
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class ReportDoneFragment : Fragment(R.layout.fragment_report_done), Injectable {
|
||||
|
@ -37,8 +38,10 @@ class ReportDoneFragment : Fragment(R.layout.fragment_report_done), Injectable {
|
|||
|
||||
private val viewModel: ReportViewModel by activityViewModels { viewModelFactory }
|
||||
|
||||
private val binding by viewBinding(FragmentReportDoneBinding::bind)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
textReported.text = getString(R.string.report_sent_success, viewModel.accountUserName)
|
||||
binding.textReported.text = getString(R.string.report_sent_success, viewModel.accountUserName)
|
||||
handleClicks()
|
||||
subscribeObservables()
|
||||
}
|
||||
|
@ -46,14 +49,14 @@ class ReportDoneFragment : Fragment(R.layout.fragment_report_done), Injectable {
|
|||
private fun subscribeObservables() {
|
||||
viewModel.muteState.observe(viewLifecycleOwner) {
|
||||
if (it !is Loading) {
|
||||
buttonMute.show()
|
||||
progressMute.show()
|
||||
binding.buttonMute.show()
|
||||
binding.progressMute.show()
|
||||
} else {
|
||||
buttonMute.hide()
|
||||
progressMute.hide()
|
||||
binding.buttonMute.hide()
|
||||
binding.progressMute.hide()
|
||||
}
|
||||
|
||||
buttonMute.setText(when (it.data) {
|
||||
binding.buttonMute.setText(when (it.data) {
|
||||
true -> R.string.action_unmute
|
||||
else -> R.string.action_mute
|
||||
})
|
||||
|
@ -61,14 +64,14 @@ class ReportDoneFragment : Fragment(R.layout.fragment_report_done), Injectable {
|
|||
|
||||
viewModel.blockState.observe(viewLifecycleOwner) {
|
||||
if (it !is Loading) {
|
||||
buttonBlock.show()
|
||||
progressBlock.show()
|
||||
binding.buttonBlock.show()
|
||||
binding.progressBlock.show()
|
||||
}
|
||||
else{
|
||||
buttonBlock.hide()
|
||||
progressBlock.hide()
|
||||
else {
|
||||
binding.buttonBlock.hide()
|
||||
binding.progressBlock.hide()
|
||||
}
|
||||
buttonBlock.setText(when (it.data) {
|
||||
binding.buttonBlock.setText(when (it.data) {
|
||||
true -> R.string.action_unblock
|
||||
else -> R.string.action_block
|
||||
})
|
||||
|
@ -77,13 +80,13 @@ class ReportDoneFragment : Fragment(R.layout.fragment_report_done), Injectable {
|
|||
}
|
||||
|
||||
private fun handleClicks() {
|
||||
buttonDone.setOnClickListener {
|
||||
binding.buttonDone.setOnClickListener {
|
||||
viewModel.navigateTo(Screen.Finish)
|
||||
}
|
||||
buttonBlock.setOnClickListener {
|
||||
binding.buttonBlock.setOnClickListener {
|
||||
viewModel.toggleBlock()
|
||||
}
|
||||
buttonMute.setOnClickListener {
|
||||
binding.buttonMute.setOnClickListener {
|
||||
viewModel.toggleMute()
|
||||
}
|
||||
}
|
||||
|
@ -91,5 +94,4 @@ class ReportDoneFragment : Fragment(R.layout.fragment_report_done), Injectable {
|
|||
companion object {
|
||||
fun newInstance() = ReportDoneFragment()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,10 +24,10 @@ import com.google.android.material.snackbar.Snackbar
|
|||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.components.report.ReportViewModel
|
||||
import com.keylesspalace.tusky.components.report.Screen
|
||||
import com.keylesspalace.tusky.databinding.FragmentReportNoteBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.util.*
|
||||
import kotlinx.android.synthetic.main.fragment_report_note.*
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -38,6 +38,8 @@ class ReportNoteFragment : Fragment(R.layout.fragment_report_note), Injectable {
|
|||
|
||||
private val viewModel: ReportViewModel by activityViewModels { viewModelFactory }
|
||||
|
||||
private val binding by viewBinding(FragmentReportNoteBinding::bind)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
fillViews()
|
||||
handleChanges()
|
||||
|
@ -46,29 +48,29 @@ class ReportNoteFragment : Fragment(R.layout.fragment_report_note), Injectable {
|
|||
}
|
||||
|
||||
private fun handleChanges() {
|
||||
editNote.doAfterTextChanged {
|
||||
binding.editNote.doAfterTextChanged {
|
||||
viewModel.reportNote = it?.toString() ?: ""
|
||||
}
|
||||
checkIsNotifyRemote.setOnCheckedChangeListener { _, isChecked ->
|
||||
binding.checkIsNotifyRemote.setOnCheckedChangeListener { _, isChecked ->
|
||||
viewModel.isRemoteNotify = isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillViews() {
|
||||
editNote.setText(viewModel.reportNote)
|
||||
binding.editNote.setText(viewModel.reportNote)
|
||||
|
||||
if (viewModel.isRemoteAccount){
|
||||
checkIsNotifyRemote.show()
|
||||
reportDescriptionRemoteInstance.show()
|
||||
binding.checkIsNotifyRemote.show()
|
||||
binding.reportDescriptionRemoteInstance.show()
|
||||
}
|
||||
else{
|
||||
checkIsNotifyRemote.hide()
|
||||
reportDescriptionRemoteInstance.hide()
|
||||
binding.checkIsNotifyRemote.hide()
|
||||
binding.reportDescriptionRemoteInstance.hide()
|
||||
}
|
||||
|
||||
if (viewModel.isRemoteAccount)
|
||||
checkIsNotifyRemote.text = getString(R.string.report_remote_instance, viewModel.remoteServer)
|
||||
checkIsNotifyRemote.isChecked = viewModel.isRemoteNotify
|
||||
binding.checkIsNotifyRemote.text = getString(R.string.report_remote_instance, viewModel.remoteServer)
|
||||
binding.checkIsNotifyRemote.isChecked = viewModel.isRemoteNotify
|
||||
}
|
||||
|
||||
private fun subscribeObservables() {
|
||||
|
@ -83,13 +85,13 @@ class ReportNoteFragment : Fragment(R.layout.fragment_report_note), Injectable {
|
|||
}
|
||||
|
||||
private fun showError(error: Throwable?) {
|
||||
editNote.isEnabled = true
|
||||
checkIsNotifyRemote.isEnabled = true
|
||||
buttonReport.isEnabled = true
|
||||
buttonBack.isEnabled = true
|
||||
progressBar.hide()
|
||||
binding.editNote.isEnabled = true
|
||||
binding.checkIsNotifyRemote.isEnabled = true
|
||||
binding.buttonReport.isEnabled = true
|
||||
binding.buttonBack.isEnabled = true
|
||||
binding.progressBar.hide()
|
||||
|
||||
Snackbar.make(buttonBack, if (error is IOException) R.string.error_network else R.string.error_generic, Snackbar.LENGTH_LONG)
|
||||
Snackbar.make(binding.buttonBack, if (error is IOException) R.string.error_network else R.string.error_generic, Snackbar.LENGTH_LONG)
|
||||
.apply {
|
||||
setAction(R.string.action_retry) {
|
||||
sendReport()
|
||||
|
@ -103,19 +105,19 @@ class ReportNoteFragment : Fragment(R.layout.fragment_report_note), Injectable {
|
|||
}
|
||||
|
||||
private fun showLoading() {
|
||||
buttonReport.isEnabled = false
|
||||
buttonBack.isEnabled = false
|
||||
editNote.isEnabled = false
|
||||
checkIsNotifyRemote.isEnabled = false
|
||||
progressBar.show()
|
||||
binding.buttonReport.isEnabled = false
|
||||
binding.buttonBack.isEnabled = false
|
||||
binding.editNote.isEnabled = false
|
||||
binding.checkIsNotifyRemote.isEnabled = false
|
||||
binding.progressBar.show()
|
||||
}
|
||||
|
||||
private fun handleClicks() {
|
||||
buttonBack.setOnClickListener {
|
||||
binding.buttonBack.setOnClickListener {
|
||||
viewModel.navigateTo(Screen.Back)
|
||||
}
|
||||
|
||||
buttonReport.setOnClickListener {
|
||||
binding.buttonReport.setOnClickListener {
|
||||
sendReport()
|
||||
}
|
||||
}
|
||||
|
@ -123,5 +125,4 @@ class ReportNoteFragment : Fragment(R.layout.fragment_report_note), Injectable {
|
|||
companion object {
|
||||
fun newInstance() = ReportNoteFragment()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.keylesspalace.tusky.components.report.ReportViewModel
|
|||
import com.keylesspalace.tusky.components.report.Screen
|
||||
import com.keylesspalace.tusky.components.report.adapter.AdapterHandler
|
||||
import com.keylesspalace.tusky.components.report.adapter.StatusesAdapter
|
||||
import com.keylesspalace.tusky.databinding.FragmentReportStatusesBinding
|
||||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
|
@ -44,8 +45,8 @@ import com.keylesspalace.tusky.util.CardViewMode
|
|||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import kotlinx.android.synthetic.main.fragment_report_statuses.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Injectable, AdapterHandler {
|
||||
|
@ -58,6 +59,8 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
|
||||
private val viewModel: ReportViewModel by activityViewModels { viewModelFactory }
|
||||
|
||||
private val binding by viewBinding(FragmentReportStatusesBinding::bind)
|
||||
|
||||
private lateinit var adapter: StatusesAdapter
|
||||
|
||||
private var snackbarErrorRetry: Snackbar? = null
|
||||
|
@ -93,9 +96,9 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
}
|
||||
|
||||
private fun setupSwipeRefreshLayout() {
|
||||
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
snackbarErrorRetry?.dismiss()
|
||||
viewModel.refreshStatuses()
|
||||
}
|
||||
|
@ -118,10 +121,10 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
adapter = StatusesAdapter(statusDisplayOptions,
|
||||
viewModel.statusViewState, this)
|
||||
|
||||
recyclerView.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL))
|
||||
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
recyclerView.adapter = adapter
|
||||
(recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
binding.recyclerView.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL))
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.recyclerView.adapter = adapter
|
||||
(binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
|
||||
viewModel.statuses.observe(viewLifecycleOwner) {
|
||||
adapter.submitList(it)
|
||||
|
@ -129,9 +132,9 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
|
||||
viewModel.networkStateAfter.observe(viewLifecycleOwner) {
|
||||
if (it?.status == com.keylesspalace.tusky.util.Status.RUNNING)
|
||||
progressBarBottom.show()
|
||||
binding.progressBarBottom.show()
|
||||
else
|
||||
progressBarBottom.hide()
|
||||
binding.progressBarBottom.hide()
|
||||
|
||||
if (it?.status == com.keylesspalace.tusky.util.Status.FAILED)
|
||||
showError(it.msg)
|
||||
|
@ -139,22 +142,22 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
|
||||
viewModel.networkStateBefore.observe(viewLifecycleOwner) {
|
||||
if (it?.status == com.keylesspalace.tusky.util.Status.RUNNING)
|
||||
progressBarTop.show()
|
||||
binding.progressBarTop.show()
|
||||
else
|
||||
progressBarTop.hide()
|
||||
binding.progressBarTop.hide()
|
||||
|
||||
if (it?.status == com.keylesspalace.tusky.util.Status.FAILED)
|
||||
showError(it.msg)
|
||||
}
|
||||
|
||||
viewModel.networkStateRefresh.observe(viewLifecycleOwner) {
|
||||
if (it?.status == com.keylesspalace.tusky.util.Status.RUNNING && !swipeRefreshLayout.isRefreshing)
|
||||
progressBarLoading.show()
|
||||
if (it?.status == com.keylesspalace.tusky.util.Status.RUNNING && !binding.swipeRefreshLayout.isRefreshing)
|
||||
binding.progressBarLoading.show()
|
||||
else
|
||||
progressBarLoading.hide()
|
||||
binding.progressBarLoading.hide()
|
||||
|
||||
if (it?.status != com.keylesspalace.tusky.util.Status.RUNNING)
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
if (it?.status == com.keylesspalace.tusky.util.Status.FAILED)
|
||||
showError(it.msg)
|
||||
}
|
||||
|
@ -162,7 +165,7 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
|
||||
private fun showError(@Suppress("UNUSED_PARAMETER") msg: String?) {
|
||||
if (snackbarErrorRetry?.isShown != true) {
|
||||
snackbarErrorRetry = Snackbar.make(swipeRefreshLayout, R.string.failed_fetch_statuses, Snackbar.LENGTH_INDEFINITE)
|
||||
snackbarErrorRetry = Snackbar.make(binding.swipeRefreshLayout, R.string.failed_fetch_statuses, Snackbar.LENGTH_INDEFINITE)
|
||||
snackbarErrorRetry?.setAction(R.string.action_retry) {
|
||||
viewModel.retryStatusLoad()
|
||||
}
|
||||
|
@ -172,11 +175,11 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
|
||||
|
||||
private fun handleClicks() {
|
||||
buttonCancel.setOnClickListener {
|
||||
binding.buttonCancel.setOnClickListener {
|
||||
viewModel.navigateTo(Screen.Back)
|
||||
}
|
||||
|
||||
buttonContinue.setOnClickListener {
|
||||
binding.buttonContinue.setOnClickListener {
|
||||
viewModel.navigateTo(Screen.Note)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,10 @@ import com.keylesspalace.tusky.components.search.adapter.SearchAccountsAdapter
|
|||
import com.keylesspalace.tusky.entity.Account
|
||||
import com.keylesspalace.tusky.settings.PrefKeys
|
||||
import com.keylesspalace.tusky.util.NetworkState
|
||||
import kotlinx.android.synthetic.main.fragment_search.*
|
||||
|
||||
class SearchAccountsFragment : SearchFragment<Account>() {
|
||||
override fun createAdapter(): PagedListAdapter<Account, *> {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context)
|
||||
|
||||
return SearchAccountsAdapter(
|
||||
this,
|
||||
|
@ -46,5 +45,4 @@ class SearchAccountsFragment : SearchFragment<Account>() {
|
|||
companion object {
|
||||
fun newInstance() = SearchAccountsFragment()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@ import com.keylesspalace.tusky.BottomSheetActivity
|
|||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.ViewTagActivity
|
||||
import com.keylesspalace.tusky.components.search.SearchViewModel
|
||||
import com.keylesspalace.tusky.databinding.FragmentSearchBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||
import com.keylesspalace.tusky.util.*
|
||||
import kotlinx.android.synthetic.main.fragment_search.*
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class SearchFragment<T> : Fragment(R.layout.fragment_search),
|
||||
|
@ -32,6 +32,8 @@ abstract class SearchFragment<T> : Fragment(R.layout.fragment_search),
|
|||
|
||||
protected val viewModel: SearchViewModel by activityViewModels { viewModelFactory }
|
||||
|
||||
protected val binding by viewBinding(FragmentSearchBinding::bind)
|
||||
|
||||
private var snackbarErrorRetry: Snackbar? = null
|
||||
|
||||
abstract fun createAdapter(): PagedListAdapter<T, *>
|
||||
|
@ -48,8 +50,8 @@ abstract class SearchFragment<T> : Fragment(R.layout.fragment_search),
|
|||
}
|
||||
|
||||
private fun setupSwipeRefreshLayout() {
|
||||
swipeRefreshLayout.setOnRefreshListener(this)
|
||||
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this)
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
}
|
||||
|
||||
private fun subscribeObservables() {
|
||||
|
@ -59,7 +61,7 @@ abstract class SearchFragment<T> : Fragment(R.layout.fragment_search),
|
|||
|
||||
networkStateRefresh.observe(viewLifecycleOwner) {
|
||||
|
||||
searchProgressBar.visible(it == NetworkState.LOADING)
|
||||
binding.searchProgressBar.visible(it == NetworkState.LOADING)
|
||||
|
||||
if (it.status == Status.FAILED) {
|
||||
showError()
|
||||
|
@ -69,7 +71,7 @@ abstract class SearchFragment<T> : Fragment(R.layout.fragment_search),
|
|||
|
||||
networkState.observe(viewLifecycleOwner) {
|
||||
|
||||
progressBarBottom.visible(it == NetworkState.LOADING)
|
||||
binding.progressBarBottom.visible(it == NetworkState.LOADING)
|
||||
|
||||
if (it.status == Status.FAILED) {
|
||||
showError()
|
||||
|
@ -82,24 +84,25 @@ abstract class SearchFragment<T> : Fragment(R.layout.fragment_search),
|
|||
}
|
||||
|
||||
private fun initAdapter() {
|
||||
searchRecyclerView.addItemDecoration(DividerItemDecoration(searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||
searchRecyclerView.layoutManager = LinearLayoutManager(searchRecyclerView.context)
|
||||
binding.searchRecyclerView.addItemDecoration(DividerItemDecoration(binding.searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||
binding.searchRecyclerView.layoutManager = LinearLayoutManager(binding.searchRecyclerView.context)
|
||||
adapter = createAdapter()
|
||||
searchRecyclerView.adapter = adapter
|
||||
searchRecyclerView.setHasFixedSize(true)
|
||||
(searchRecyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
binding.searchRecyclerView.adapter = adapter
|
||||
binding.searchRecyclerView.setHasFixedSize(true)
|
||||
(binding.searchRecyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
|
||||
private fun showNoData(isEmpty: Boolean) {
|
||||
if (isEmpty && networkStateRefresh.value == NetworkState.LOADED)
|
||||
searchNoResultsText.show()
|
||||
else
|
||||
searchNoResultsText.hide()
|
||||
if (isEmpty && networkStateRefresh.value == NetworkState.LOADED) {
|
||||
binding.searchNoResultsText.show()
|
||||
} else {
|
||||
binding.searchNoResultsText.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showError() {
|
||||
if (snackbarErrorRetry?.isShown != true) {
|
||||
snackbarErrorRetry = Snackbar.make(layoutRoot, R.string.failed_search, Snackbar.LENGTH_INDEFINITE)
|
||||
snackbarErrorRetry = Snackbar.make(binding.root, R.string.failed_search, Snackbar.LENGTH_INDEFINITE)
|
||||
snackbarErrorRetry?.setAction(R.string.action_retry) {
|
||||
snackbarErrorRetry = null
|
||||
viewModel.retryAllSearches()
|
||||
|
@ -122,8 +125,8 @@ abstract class SearchFragment<T> : Fragment(R.layout.fragment_search),
|
|||
override fun onRefresh() {
|
||||
|
||||
// Dismissed here because the RecyclerView bottomProgressBar is shown as soon as the retry begins.
|
||||
swipeRefreshLayout.post {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
binding.swipeRefreshLayout.post {
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
viewModel.retryAllSearches()
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@ import com.keylesspalace.tusky.viewdata.StatusViewData
|
|||
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
|
||||
import com.uber.autodispose.autoDispose
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.fragment_search.*
|
||||
|
||||
class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concrete>>(), StatusActionListener {
|
||||
|
||||
|
@ -78,7 +77,7 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
|||
get() = super.adapter as SearchStatusesAdapter
|
||||
|
||||
override fun createAdapter(): PagedListAdapter<Pair<Status, StatusViewData.Concrete>, *> {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context)
|
||||
val statusDisplayOptions = StatusDisplayOptions(
|
||||
animateAvatars = preferences.getBoolean("animateGifAvatars", false),
|
||||
mediaPreviewEnabled = viewModel.mediaPreviewEnabled,
|
||||
|
@ -91,12 +90,11 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
|||
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||
)
|
||||
|
||||
searchRecyclerView.addItemDecoration(DividerItemDecoration(searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||
searchRecyclerView.layoutManager = LinearLayoutManager(searchRecyclerView.context)
|
||||
binding.searchRecyclerView.addItemDecoration(DividerItemDecoration(binding.searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||
binding.searchRecyclerView.layoutManager = LinearLayoutManager(binding.searchRecyclerView.context)
|
||||
return SearchStatusesAdapter(statusDisplayOptions, this)
|
||||
}
|
||||
|
||||
|
||||
override fun onContentHiddenChange(isShowing: Boolean, position: Int) {
|
||||
searchAdapter.getItem(position)?.let {
|
||||
viewModel.contentHiddenChange(it, isShowing)
|
||||
|
@ -486,5 +484,4 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
|||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.keylesspalace.tusky.AccountListActivity.Type
|
|||
import com.keylesspalace.tusky.BaseActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.adapter.*
|
||||
import com.keylesspalace.tusky.databinding.FragmentAccountListBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
import com.keylesspalace.tusky.entity.Relationship
|
||||
|
@ -40,12 +41,12 @@ import com.keylesspalace.tusky.settings.PrefKeys
|
|||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.view.EndlessOnScrollListener
|
||||
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
|
||||
import com.uber.autodispose.autoDispose
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.fragment_account_list.*
|
||||
import retrofit2.Response
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
@ -56,6 +57,8 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
@Inject
|
||||
lateinit var api: MastodonApi
|
||||
|
||||
private val binding by viewBinding(FragmentAccountListBinding::bind)
|
||||
|
||||
private lateinit var type: Type
|
||||
private var id: String? = null
|
||||
|
||||
|
@ -73,12 +76,12 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
val layoutManager = LinearLayoutManager(view.context)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
(recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
(binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
|
||||
recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||
binding.recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||
|
||||
val pm = PreferenceManager.getDefaultSharedPreferences(view.context)
|
||||
val animateAvatar = pm.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false)
|
||||
|
@ -90,7 +93,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
Type.FOLLOW_REQUESTS -> FollowRequestsAdapter(this, animateAvatar, animateEmojis)
|
||||
else -> FollowAdapter(this, animateAvatar, animateEmojis)
|
||||
}
|
||||
recyclerView.adapter = adapter
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
scrollListener = object : EndlessOnScrollListener(layoutManager) {
|
||||
override fun onLoadMore(totalItemsCount: Int, view: RecyclerView) {
|
||||
|
@ -101,7 +104,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
}
|
||||
}
|
||||
|
||||
recyclerView.addOnScrollListener(scrollListener)
|
||||
binding.recyclerView.addOnScrollListener(scrollListener)
|
||||
|
||||
fetchAccounts()
|
||||
}
|
||||
|
@ -136,7 +139,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
val unmutedUser = mutesAdapter.removeItem(position)
|
||||
|
||||
if (unmutedUser != null) {
|
||||
Snackbar.make(recyclerView, R.string.confirmation_unmuted, Snackbar.LENGTH_LONG)
|
||||
Snackbar.make(binding.recyclerView, R.string.confirmation_unmuted, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_undo) {
|
||||
mutesAdapter.addItem(unmutedUser, position)
|
||||
onMute(true, id, position, notifications)
|
||||
|
@ -180,7 +183,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
val unblockedUser = blocksAdapter.removeItem(position)
|
||||
|
||||
if (unblockedUser != null) {
|
||||
Snackbar.make(recyclerView, R.string.confirmation_unblocked, Snackbar.LENGTH_LONG)
|
||||
Snackbar.make(binding.recyclerView, R.string.confirmation_unblocked, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_undo) {
|
||||
blocksAdapter.addItem(unblockedUser, position)
|
||||
onBlock(true, id, position)
|
||||
|
@ -260,7 +263,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
fetching = true
|
||||
|
||||
if (fromId != null) {
|
||||
recyclerView.post { adapter.setBottomLoading(true) }
|
||||
binding.recyclerView.post { adapter.setBottomLoading(true) }
|
||||
}
|
||||
|
||||
getFetchCallByListType(fromId)
|
||||
|
@ -303,14 +306,14 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
fetching = false
|
||||
|
||||
if (adapter.itemCount == 0) {
|
||||
messageView.show()
|
||||
messageView.setup(
|
||||
binding.messageView.show()
|
||||
binding.messageView.setup(
|
||||
R.drawable.elephant_friend_empty,
|
||||
R.string.message_empty,
|
||||
null
|
||||
)
|
||||
} else {
|
||||
messageView.hide()
|
||||
binding.messageView.hide()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,15 +342,15 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
Log.e(TAG, "Fetch failure", throwable)
|
||||
|
||||
if (adapter.itemCount == 0) {
|
||||
messageView.show()
|
||||
binding.messageView.show()
|
||||
if (throwable is IOException) {
|
||||
messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
messageView.hide()
|
||||
binding.messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
binding.messageView.hide()
|
||||
this.fetchAccounts(null)
|
||||
}
|
||||
} else {
|
||||
messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
messageView.hide()
|
||||
binding.messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
binding.messageView.hide()
|
||||
this.fetchAccounts(null)
|
||||
}
|
||||
}
|
||||
|
@ -368,5 +371,4 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.bumptech.glide.Glide
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.ViewMediaActivity
|
||||
import com.keylesspalace.tusky.databinding.FragmentTimelineBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
|
@ -39,13 +40,13 @@ import com.keylesspalace.tusky.util.LinkHelper
|
|||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.view.SquareImageView
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import com.uber.autodispose.android.lifecycle.autoDispose
|
||||
import io.reactivex.SingleObserver
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlinx.android.synthetic.main.fragment_timeline.*
|
||||
import retrofit2.Response
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
@ -58,49 +59,36 @@ import javax.inject.Inject
|
|||
*/
|
||||
|
||||
class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFragment, Injectable {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(accountId: String, enableSwipeToRefresh:Boolean=true): AccountMediaFragment {
|
||||
val fragment = AccountMediaFragment()
|
||||
val args = Bundle()
|
||||
args.putString(ACCOUNT_ID_ARG, accountId)
|
||||
args.putBoolean(ARG_ENABLE_SWIPE_TO_REFRESH,enableSwipeToRefresh)
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
|
||||
private const val ACCOUNT_ID_ARG = "account_id"
|
||||
private const val TAG = "AccountMediaFragment"
|
||||
private const val ARG_ENABLE_SWIPE_TO_REFRESH = "arg.enable.swipe.to.refresh"
|
||||
}
|
||||
|
||||
private var isSwipeToRefreshEnabled: Boolean = true
|
||||
private var needToRefresh = false
|
||||
|
||||
@Inject
|
||||
lateinit var api: MastodonApi
|
||||
|
||||
private val binding by viewBinding(FragmentTimelineBinding::bind)
|
||||
|
||||
private lateinit var accountId: String
|
||||
|
||||
private val adapter = MediaGridAdapter()
|
||||
private val statuses = mutableListOf<Status>()
|
||||
private var fetchingStatus = FetchingStatus.NOT_FETCHING
|
||||
|
||||
private lateinit var accountId: String
|
||||
private var isSwipeToRefreshEnabled: Boolean = true
|
||||
private var needToRefresh = false
|
||||
|
||||
private val callback = object : SingleObserver<Response<List<Status>>> {
|
||||
override fun onError(t: Throwable) {
|
||||
fetchingStatus = FetchingStatus.NOT_FETCHING
|
||||
|
||||
if (isAdded) {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
progressBar.visibility = View.GONE
|
||||
topProgressBar?.hide()
|
||||
statusView.show()
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.topProgressBar.hide()
|
||||
binding.statusView.show()
|
||||
if (t is IOException) {
|
||||
statusView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
doInitialLoadingIfNeeded()
|
||||
}
|
||||
} else {
|
||||
statusView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
doInitialLoadingIfNeeded()
|
||||
}
|
||||
}
|
||||
|
@ -112,9 +100,9 @@ class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFr
|
|||
override fun onSuccess(response: Response<List<Status>>) {
|
||||
fetchingStatus = FetchingStatus.NOT_FETCHING
|
||||
if (isAdded) {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
progressBar.visibility = View.GONE
|
||||
topProgressBar?.hide()
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.topProgressBar.hide()
|
||||
|
||||
val body = response.body()
|
||||
body?.let { fetched ->
|
||||
|
@ -126,11 +114,11 @@ class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFr
|
|||
}
|
||||
adapter.addTop(result)
|
||||
if (result.isNotEmpty())
|
||||
recyclerView.scrollToPosition(0)
|
||||
binding.recyclerView.scrollToPosition(0)
|
||||
|
||||
if (statuses.isEmpty()) {
|
||||
statusView.show()
|
||||
statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty)
|
||||
binding.statusView.show()
|
||||
binding.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,18 +169,18 @@ class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFr
|
|||
|
||||
adapter.baseItemColor = ThemeUtils.getColor(view.context, android.R.attr.windowBackground)
|
||||
|
||||
recyclerView.layoutManager = layoutManager
|
||||
recyclerView.adapter = adapter
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
if (isSwipeToRefreshEnabled) {
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
refresh()
|
||||
}
|
||||
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
}
|
||||
statusView.visibility = View.GONE
|
||||
binding.statusView.visibility = View.GONE
|
||||
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
|
||||
override fun onScrolled(recycler_view: RecyclerView, dx: Int, dy: Int) {
|
||||
if (dy > 0) {
|
||||
|
@ -216,7 +204,7 @@ class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFr
|
|||
}
|
||||
|
||||
private fun refresh() {
|
||||
statusView.hide()
|
||||
binding.statusView.hide()
|
||||
if (fetchingStatus != FetchingStatus.NOT_FETCHING) return
|
||||
if (statuses.isEmpty()) {
|
||||
fetchingStatus = FetchingStatus.INITIAL_FETCHING
|
||||
|
@ -229,12 +217,12 @@ class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFr
|
|||
.subscribe(callback)
|
||||
|
||||
if (!isSwipeToRefreshEnabled)
|
||||
topProgressBar?.show()
|
||||
binding.topProgressBar.show()
|
||||
}
|
||||
|
||||
private fun doInitialLoadingIfNeeded() {
|
||||
if (isAdded) {
|
||||
statusView.hide()
|
||||
binding.statusView.hide()
|
||||
}
|
||||
if (fetchingStatus == FetchingStatus.NOT_FETCHING && statuses.isEmpty()) {
|
||||
fetchingStatus = FetchingStatus.INITIAL_FETCHING
|
||||
|
@ -344,4 +332,19 @@ class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFr
|
|||
needToRefresh = true
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(accountId: String, enableSwipeToRefresh:Boolean=true): AccountMediaFragment {
|
||||
val fragment = AccountMediaFragment()
|
||||
val args = Bundle()
|
||||
args.putString(ACCOUNT_ID_ARG, accountId)
|
||||
args.putBoolean(ARG_ENABLE_SWIPE_TO_REFRESH,enableSwipeToRefresh)
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
|
||||
private const val ACCOUNT_ID_ARG = "account_id"
|
||||
private const val TAG = "AccountMediaFragment"
|
||||
private const val ARG_ENABLE_SWIPE_TO_REFRESH = "arg.enable.swipe.to.refresh"
|
||||
}
|
||||
}
|
|
@ -32,13 +32,12 @@ import com.bumptech.glide.load.engine.GlideException
|
|||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.github.chrisbanes.photoview.PhotoViewAttacher
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.ViewMediaActivity
|
||||
import com.keylesspalace.tusky.databinding.FragmentViewImageBinding
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import kotlinx.android.synthetic.main.activity_view_media.*
|
||||
import kotlinx.android.synthetic.main.fragment_view_image.*
|
||||
import kotlin.math.abs
|
||||
|
||||
class ViewImageFragment : ViewMediaFragment() {
|
||||
|
@ -48,6 +47,9 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
fun onPhotoTap()
|
||||
}
|
||||
|
||||
private var _binding: FragmentViewImageBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var attacher: PhotoViewAttacher
|
||||
private lateinit var photoActionsListener: PhotoActionsListener
|
||||
private lateinit var toolbar: View
|
||||
|
@ -71,18 +73,19 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
description: String?,
|
||||
showingDescription: Boolean
|
||||
) {
|
||||
photoView.transitionName = url
|
||||
mediaDescription.text = description
|
||||
captionSheet.visible(showingDescription)
|
||||
binding.photoView.transitionName = url
|
||||
binding.mediaDescription.text = description
|
||||
binding.captionSheet.visible(showingDescription)
|
||||
|
||||
startedTransition = false
|
||||
loadImageFromNetwork(url, previewUrl, photoView)
|
||||
loadImageFromNetwork(url, previewUrl, binding.photoView)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
toolbar = requireActivity().toolbar
|
||||
toolbar = (requireActivity() as ViewMediaActivity).toolbar
|
||||
this.transition = BehaviorSubject.create()
|
||||
return inflater.inflate(R.layout.fragment_view_image, container, false)
|
||||
_binding = FragmentViewImageBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
|
@ -105,7 +108,7 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
attacher = PhotoViewAttacher(photoView).apply {
|
||||
attacher = PhotoViewAttacher(binding.photoView).apply {
|
||||
// This prevents conflicts with ViewPager
|
||||
setAllowParentInterceptOnEdge(true)
|
||||
|
||||
|
@ -127,7 +130,7 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
|
||||
var lastY = 0f
|
||||
|
||||
photoView.setOnTouchListener { v, event ->
|
||||
binding.photoView.setOnTouchListener { v, event ->
|
||||
// This part is for scaling/translating on vertical move.
|
||||
// We use raw coordinates to get the correct ones during scaling
|
||||
|
||||
|
@ -140,11 +143,11 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
val diff = event.rawY - lastY
|
||||
// This code is to prevent transformations during page scrolling
|
||||
// If we are already translating or we reached the threshold, then transform.
|
||||
if (photoView.translationY != 0f || abs(diff) > 40) {
|
||||
photoView.translationY += (diff)
|
||||
val scale = (-abs(photoView.translationY) / 720 + 1).coerceAtLeast(0.5f)
|
||||
photoView.scaleY = scale
|
||||
photoView.scaleX = scale
|
||||
if (binding.photoView.translationY != 0f || abs(diff) > 40) {
|
||||
binding.photoView.translationY += (diff)
|
||||
val scale = (-abs(binding.photoView.translationY) / 720 + 1).coerceAtLeast(0.5f)
|
||||
binding.photoView.scaleY = scale
|
||||
binding.photoView.scaleX = scale
|
||||
lastY = event.rawY
|
||||
return@setOnTouchListener true
|
||||
}
|
||||
|
@ -158,13 +161,13 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
}
|
||||
|
||||
private fun onGestureEnd() {
|
||||
if (photoView == null) {
|
||||
if (_binding == null) {
|
||||
return
|
||||
}
|
||||
if (abs(photoView.translationY) > 180) {
|
||||
if (abs(binding.photoView.translationY) > 180) {
|
||||
photoActionsListener.onDismiss()
|
||||
} else {
|
||||
photoView.animate().translationY(0f).scaleX(1f).scaleY(1f).start()
|
||||
binding.photoView.animate().translationY(0f).scaleX(1f).scaleY(1f).start()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,15 +176,17 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
}
|
||||
|
||||
override fun onToolbarVisibilityChange(visible: Boolean) {
|
||||
if (photoView == null || !userVisibleHint || captionSheet == null) {
|
||||
if (_binding == null || !userVisibleHint ) {
|
||||
return
|
||||
}
|
||||
isDescriptionVisible = showingDescription && visible
|
||||
val alpha = if (isDescriptionVisible) 1.0f else 0.0f
|
||||
captionSheet.animate().alpha(alpha)
|
||||
binding.captionSheet.animate().alpha(alpha)
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
captionSheet?.visible(isDescriptionVisible)
|
||||
if (_binding != null) {
|
||||
binding.captionSheet.visible(isDescriptionVisible)
|
||||
}
|
||||
animation.removeListener(this)
|
||||
}
|
||||
})
|
||||
|
@ -189,8 +194,9 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
Glide.with(this).clear(photoView)
|
||||
Glide.with(this).clear(binding.photoView)
|
||||
transition.onComplete()
|
||||
_binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
|
@ -253,7 +259,7 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
photoActionsListener.onBringUp()
|
||||
}
|
||||
// Hide progress bar only on fail request from internet
|
||||
if (!isCacheRequest) progressBar?.hide()
|
||||
if (!isCacheRequest && _binding != null) binding.progressBar.hide()
|
||||
// We don't want to overwrite preview with null when main image fails to load
|
||||
return !isCacheRequest
|
||||
}
|
||||
|
@ -261,14 +267,16 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
@SuppressLint("CheckResult")
|
||||
override fun onResourceReady(resource: Drawable, model: Any, target: Target<Drawable>,
|
||||
dataSource: DataSource, isFirstResource: Boolean): Boolean {
|
||||
progressBar?.hide() // Always hide the progress bar on success
|
||||
if (_binding != null) {
|
||||
binding.progressBar.hide() // Always hide the progress bar on success
|
||||
}
|
||||
|
||||
if (!startedTransition || !shouldStartTransition) {
|
||||
// Set this right away so that we don't have to concurrent post() requests
|
||||
startedTransition = true
|
||||
// post() because load() replaces image with null. Sometimes after we set
|
||||
// the thumbnail.
|
||||
photoView.post {
|
||||
binding.photoView.post {
|
||||
target.onResourceReady(resource, null)
|
||||
if (shouldStartTransition) photoActionsListener.onBringUp()
|
||||
}
|
||||
|
|
|
@ -26,16 +26,18 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.MediaController
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.ViewMediaActivity
|
||||
import com.keylesspalace.tusky.databinding.FragmentViewVideoBinding
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import com.keylesspalace.tusky.view.ExposedPlayPauseVideoView
|
||||
import kotlinx.android.synthetic.main.activity_view_media.*
|
||||
import kotlinx.android.synthetic.main.fragment_view_video.*
|
||||
|
||||
class ViewVideoFragment : ViewMediaFragment() {
|
||||
|
||||
private var _binding: FragmentViewVideoBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var toolbar: View
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val hideToolbar = Runnable {
|
||||
|
@ -52,7 +54,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||
// Start/pause/resume video playback as fragment is shown/hidden
|
||||
super.setUserVisibleHint(isVisibleToUser)
|
||||
if (videoView == null) {
|
||||
if (_binding == null) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -60,10 +62,10 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
if (mediaActivity.isToolbarVisible) {
|
||||
handler.postDelayed(hideToolbar, TOOLBAR_HIDE_DELAY_MS)
|
||||
}
|
||||
videoView.start()
|
||||
binding.videoView.start()
|
||||
} else {
|
||||
handler.removeCallbacks(hideToolbar)
|
||||
videoView.pause()
|
||||
binding.videoView.pause()
|
||||
mediaController.hide()
|
||||
}
|
||||
}
|
||||
|
@ -75,11 +77,11 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
description: String?,
|
||||
showingDescription: Boolean
|
||||
) {
|
||||
mediaDescription.text = description
|
||||
mediaDescription.visible(showingDescription)
|
||||
binding.mediaDescription.text = description
|
||||
binding.mediaDescription.visible(showingDescription)
|
||||
|
||||
videoView.transitionName = url
|
||||
videoView.setVideoPath(url)
|
||||
binding.videoView.transitionName = url
|
||||
binding.videoView.setVideoPath(url)
|
||||
mediaController = object : MediaController(mediaActivity) {
|
||||
override fun show(timeout: Int) {
|
||||
// We're doing manual auto-close management.
|
||||
|
@ -100,10 +102,10 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
mediaController.setMediaPlayer(videoView)
|
||||
videoView.setMediaController(mediaController)
|
||||
videoView.requestFocus()
|
||||
videoView.setPlayPauseListener(object: ExposedPlayPauseVideoView.PlayPauseListener {
|
||||
mediaController.setMediaPlayer(binding.videoView)
|
||||
binding.videoView.setMediaController(mediaController)
|
||||
binding.videoView.requestFocus()
|
||||
binding.videoView.setPlayPauseListener(object: ExposedPlayPauseVideoView.PlayPauseListener {
|
||||
override fun onPause() {
|
||||
handler.removeCallbacks(hideToolbar)
|
||||
}
|
||||
|
@ -117,31 +119,31 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
}
|
||||
}
|
||||
})
|
||||
videoView.setOnPreparedListener { mp ->
|
||||
val containerWidth = videoContainer.measuredWidth.toFloat()
|
||||
val containerHeight = videoContainer.measuredHeight.toFloat()
|
||||
binding.videoView.setOnPreparedListener { mp ->
|
||||
val containerWidth = binding.videoContainer.measuredWidth.toFloat()
|
||||
val containerHeight = binding.videoContainer.measuredHeight.toFloat()
|
||||
val videoWidth = mp.videoWidth.toFloat()
|
||||
val videoHeight = mp.videoHeight.toFloat()
|
||||
|
||||
if(containerWidth/containerHeight > videoWidth/videoHeight) {
|
||||
videoView.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
videoView.layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
binding.videoView.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
binding.videoView.layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
} else {
|
||||
videoView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
videoView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
binding.videoView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
binding.videoView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
|
||||
// Wait until the media is loaded before accepting taps as we don't want toolbar to
|
||||
// be hidden until then.
|
||||
videoView.setOnTouchListener { _, _ ->
|
||||
binding.videoView.setOnTouchListener { _, _ ->
|
||||
mediaActivity.onPhotoTap()
|
||||
false
|
||||
}
|
||||
|
||||
progressBar.hide()
|
||||
binding.progressBar.hide()
|
||||
mp.isLooping = true
|
||||
if (requireArguments().getBoolean(ARG_START_POSTPONED_TRANSITION)) {
|
||||
videoView.start()
|
||||
binding.videoView.start()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,9 +157,10 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
toolbar = requireActivity().toolbar
|
||||
mediaActivity = activity as ViewMediaActivity
|
||||
return inflater.inflate(R.layout.fragment_view_video, container, false)
|
||||
toolbar = mediaActivity.toolbar
|
||||
_binding = FragmentViewVideoBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -174,7 +177,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
}
|
||||
|
||||
override fun onToolbarVisibilityChange(visible: Boolean) {
|
||||
if (videoView == null || mediaDescription == null || !userVisibleHint) {
|
||||
if (_binding == null || !userVisibleHint) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -182,20 +185,22 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
val alpha = if (isDescriptionVisible) 1.0f else 0.0f
|
||||
if (isDescriptionVisible) {
|
||||
// If to be visible, need to make visible immediately and animate alpha
|
||||
mediaDescription.alpha = 0.0f
|
||||
mediaDescription.visible(isDescriptionVisible)
|
||||
binding.mediaDescription.alpha = 0.0f
|
||||
binding.mediaDescription.visible(isDescriptionVisible)
|
||||
}
|
||||
|
||||
mediaDescription.animate().alpha(alpha)
|
||||
binding.mediaDescription.animate().alpha(alpha)
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
mediaDescription?.visible(isDescriptionVisible)
|
||||
if (_binding != null) {
|
||||
binding.mediaDescription.visible(isDescriptionVisible)
|
||||
}
|
||||
animation.removeListener(this)
|
||||
}
|
||||
})
|
||||
.start()
|
||||
|
||||
if (visible && videoView.isPlaying && !isAudio) {
|
||||
if (visible && binding.videoView.isPlaying && !isAudio) {
|
||||
hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
|
||||
} else {
|
||||
handler.removeCallbacks(hideToolbar)
|
||||
|
@ -204,4 +209,9 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
|
||||
override fun onTransitionEnd() {
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
package com.keylesspalace.tusky.util
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* https://medium.com/@Zhuinden/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c
|
||||
|
@ -13,3 +20,48 @@ inline fun <T : ViewBinding> AppCompatActivity.viewBinding(
|
|||
) = lazy(LazyThreadSafetyMode.NONE) {
|
||||
bindingInflater(layoutInflater)
|
||||
}
|
||||
|
||||
class FragmentViewBindingDelegate<T : ViewBinding>(
|
||||
val fragment: Fragment,
|
||||
val viewBindingFactory: (View) -> T
|
||||
) : ReadOnlyProperty<Fragment, T> {
|
||||
private var binding: T? = null
|
||||
|
||||
init {
|
||||
fragment.lifecycle.addObserver(
|
||||
object : DefaultLifecycleObserver {
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
fragment.viewLifecycleOwnerLiveData.observe(
|
||||
fragment,
|
||||
{ t ->
|
||||
t?.lifecycle?.addObserver(
|
||||
object : DefaultLifecycleObserver {
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
binding = null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
|
||||
val binding = binding
|
||||
if (binding != null) {
|
||||
return binding
|
||||
}
|
||||
|
||||
val lifecycle = fragment.viewLifecycleOwner.lifecycle
|
||||
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
|
||||
throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
|
||||
}
|
||||
|
||||
return viewBindingFactory(thisRef.requireView()).also { this@FragmentViewBindingDelegate.binding = it }
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
|
||||
FragmentViewBindingDelegate(this, viewBindingFactory)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/layoutRoot"
|
||||
android:layout_width="@dimen/timeline_width"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
|
|
@ -32,12 +32,12 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible"
|
||||
app:layout_constrainedHeight="true" />
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.core.widget.ContentLoadingProgressBar
|
||||
android:id="@+id/topProgressBar"
|
||||
|
@ -50,4 +50,5 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in a new issue