Fix search bugs (#1624)
* fix toggling media visibility * cleanup search code to make it more readable * remove redundant OnQueryTextListener this is the default behavior * fix bookmarking * fix status interaction causing unnecessary network requests
This commit is contained in:
parent
f8c7bedfa6
commit
7cb76aad97
|
@ -28,13 +28,12 @@ import com.keylesspalace.tusky.BottomSheetActivity
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
import com.keylesspalace.tusky.components.search.adapter.SearchPagerAdapter
|
import com.keylesspalace.tusky.components.search.adapter.SearchPagerAdapter
|
||||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||||
import dagger.android.AndroidInjector
|
|
||||||
import dagger.android.DispatchingAndroidInjector
|
import dagger.android.DispatchingAndroidInjector
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import kotlinx.android.synthetic.main.activity_search.*
|
import kotlinx.android.synthetic.main.activity_search.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class SearchActivity : BottomSheetActivity(), SearchView.OnQueryTextListener, HasAndroidInjector {
|
class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var androidInjector: DispatchingAndroidInjector<Any>
|
lateinit var androidInjector: DispatchingAndroidInjector<Any>
|
||||||
|
|
||||||
|
@ -94,14 +93,6 @@ class SearchActivity : BottomSheetActivity(), SearchView.OnQueryTextListener, Ha
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onQueryTextSubmit(query: String): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPageTitle(position: Int): CharSequence? {
|
private fun getPageTitle(position: Int): CharSequence? {
|
||||||
return when (position) {
|
return when (position) {
|
||||||
0 -> getString(R.string.title_statuses)
|
0 -> getString(R.string.title_statuses)
|
||||||
|
@ -123,15 +114,12 @@ class SearchActivity : BottomSheetActivity(), SearchView.OnQueryTextListener, Ha
|
||||||
|
|
||||||
searchView.setSearchableInfo((getSystemService(Context.SEARCH_SERVICE) as? SearchManager)?.getSearchableInfo(componentName))
|
searchView.setSearchableInfo((getSystemService(Context.SEARCH_SERVICE) as? SearchManager)?.getSearchableInfo(componentName))
|
||||||
|
|
||||||
searchView.setOnQueryTextListener(this)
|
|
||||||
searchView.requestFocus()
|
searchView.requestFocus()
|
||||||
|
|
||||||
searchView.maxWidth = Integer.MAX_VALUE
|
searchView.maxWidth = Integer.MAX_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun androidInjector(): AndroidInjector<Any>? {
|
override fun androidInjector() = androidInjector
|
||||||
return androidInjector
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|
|
@ -3,18 +3,15 @@ package com.keylesspalace.tusky.components.search
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Transformations
|
|
||||||
import androidx.paging.PagedList
|
import androidx.paging.PagedList
|
||||||
import com.keylesspalace.tusky.components.search.adapter.SearchRepository
|
import com.keylesspalace.tusky.components.search.adapter.SearchRepository
|
||||||
import com.keylesspalace.tusky.db.AccountEntity
|
import com.keylesspalace.tusky.db.AccountEntity
|
||||||
import com.keylesspalace.tusky.db.AccountManager
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
import com.keylesspalace.tusky.entity.*
|
import com.keylesspalace.tusky.entity.*
|
||||||
|
import com.keylesspalace.tusky.entity.Status
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.network.TimelineCases
|
import com.keylesspalace.tusky.network.TimelineCases
|
||||||
import com.keylesspalace.tusky.util.Listing
|
import com.keylesspalace.tusky.util.*
|
||||||
import com.keylesspalace.tusky.util.NetworkState
|
|
||||||
import com.keylesspalace.tusky.util.RxAwareViewModel
|
|
||||||
import com.keylesspalace.tusky.util.ViewDataUtils
|
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
@ -23,7 +20,8 @@ import javax.inject.Inject
|
||||||
class SearchViewModel @Inject constructor(
|
class SearchViewModel @Inject constructor(
|
||||||
mastodonApi: MastodonApi,
|
mastodonApi: MastodonApi,
|
||||||
private val timelineCases: TimelineCases,
|
private val timelineCases: TimelineCases,
|
||||||
private val accountManager: AccountManager) : RxAwareViewModel() {
|
private val accountManager: AccountManager
|
||||||
|
) : RxAwareViewModel() {
|
||||||
|
|
||||||
var currentQuery: String = ""
|
var currentQuery: String = ""
|
||||||
|
|
||||||
|
@ -33,49 +31,46 @@ class SearchViewModel @Inject constructor(
|
||||||
accountManager.activeAccount = value
|
accountManager.activeAccount = value
|
||||||
}
|
}
|
||||||
|
|
||||||
val mediaPreviewEnabled: Boolean
|
val mediaPreviewEnabled = activeAccount?.mediaPreviewEnabled ?: false
|
||||||
get() = activeAccount?.mediaPreviewEnabled ?: false
|
val alwaysShowSensitiveMedia = activeAccount?.alwaysShowSensitiveMedia ?: false
|
||||||
|
val alwaysOpenSpoiler = activeAccount?.alwaysOpenSpoiler ?: false
|
||||||
|
|
||||||
private val statusesRepository = SearchRepository<Pair<Status, StatusViewData.Concrete>>(mastodonApi)
|
private val statusesRepository = SearchRepository<Pair<Status, StatusViewData.Concrete>>(mastodonApi)
|
||||||
private val accountsRepository = SearchRepository<Account>(mastodonApi)
|
private val accountsRepository = SearchRepository<Account>(mastodonApi)
|
||||||
private val hashtagsRepository = SearchRepository<HashTag>(mastodonApi)
|
private val hashtagsRepository = SearchRepository<HashTag>(mastodonApi)
|
||||||
val alwaysShowSensitiveMedia: Boolean = activeAccount?.alwaysShowSensitiveMedia
|
|
||||||
?: false
|
|
||||||
val alwaysOpenSpoiler: Boolean = activeAccount?.alwaysOpenSpoiler
|
|
||||||
?: false
|
|
||||||
|
|
||||||
private val repoResultStatus = MutableLiveData<Listing<Pair<Status, StatusViewData.Concrete>>>()
|
private val repoResultStatus = MutableLiveData<Listing<Pair<Status, StatusViewData.Concrete>>>()
|
||||||
val statuses: LiveData<PagedList<Pair<Status, StatusViewData.Concrete>>> = Transformations.switchMap(repoResultStatus) { it.pagedList }
|
val statuses: LiveData<PagedList<Pair<Status, StatusViewData.Concrete>>> = repoResultStatus.switchMap { it.pagedList }
|
||||||
val networkStateStatus: LiveData<NetworkState> = Transformations.switchMap(repoResultStatus) { it.networkState }
|
val networkStateStatus: LiveData<NetworkState> = repoResultStatus.switchMap { it.networkState }
|
||||||
val networkStateStatusRefresh: LiveData<NetworkState> = Transformations.switchMap(repoResultStatus) { it.refreshState }
|
val networkStateStatusRefresh: LiveData<NetworkState> = repoResultStatus.switchMap { it.refreshState }
|
||||||
|
|
||||||
private val repoResultAccount = MutableLiveData<Listing<Account>>()
|
private val repoResultAccount = MutableLiveData<Listing<Account>>()
|
||||||
val accounts: LiveData<PagedList<Account>> = Transformations.switchMap(repoResultAccount) { it.pagedList }
|
val accounts: LiveData<PagedList<Account>> = repoResultAccount.switchMap { it.pagedList }
|
||||||
val networkStateAccount: LiveData<NetworkState> = Transformations.switchMap(repoResultAccount) { it.networkState }
|
val networkStateAccount: LiveData<NetworkState> = repoResultAccount.switchMap { it.networkState }
|
||||||
val networkStateAccountRefresh: LiveData<NetworkState> = Transformations.switchMap(repoResultAccount) { it.refreshState }
|
val networkStateAccountRefresh: LiveData<NetworkState> = repoResultAccount.switchMap { it.refreshState }
|
||||||
|
|
||||||
private val repoResultHashTag = MutableLiveData<Listing<HashTag>>()
|
private val repoResultHashTag = MutableLiveData<Listing<HashTag>>()
|
||||||
val hashtags: LiveData<PagedList<HashTag>> = Transformations.switchMap(repoResultHashTag) { it.pagedList }
|
val hashtags: LiveData<PagedList<HashTag>> = repoResultHashTag.switchMap { it.pagedList }
|
||||||
val networkStateHashTag: LiveData<NetworkState> = Transformations.switchMap(repoResultHashTag) { it.networkState }
|
val networkStateHashTag: LiveData<NetworkState> = repoResultHashTag.switchMap { it.networkState }
|
||||||
val networkStateHashTagRefresh: LiveData<NetworkState> = Transformations.switchMap(repoResultHashTag) { it.refreshState }
|
val networkStateHashTagRefresh: LiveData<NetworkState> = repoResultHashTag.switchMap { it.refreshState }
|
||||||
|
|
||||||
private val loadedStatuses = ArrayList<Pair<Status, StatusViewData.Concrete>>()
|
private val loadedStatuses = ArrayList<Pair<Status, StatusViewData.Concrete>>()
|
||||||
fun search(query: String) {
|
fun search(query: String) {
|
||||||
loadedStatuses.clear()
|
loadedStatuses.clear()
|
||||||
repoResultStatus.value = statusesRepository.getSearchData(SearchType.Status, query, disposables, initialItems = loadedStatuses) {
|
repoResultStatus.value = statusesRepository.getSearchData(SearchType.Status, query, disposables, initialItems = loadedStatuses) {
|
||||||
(it?.statuses?.map { status -> Pair(status, ViewDataUtils.statusToViewData(status, alwaysShowSensitiveMedia, alwaysOpenSpoiler)!!) }
|
it?.statuses?.map { status -> Pair(status, ViewDataUtils.statusToViewData(status, alwaysShowSensitiveMedia, alwaysOpenSpoiler)!!) }
|
||||||
?: emptyList())
|
.orEmpty()
|
||||||
.apply {
|
.apply {
|
||||||
loadedStatuses.addAll(this)
|
loadedStatuses.addAll(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repoResultAccount.value = accountsRepository.getSearchData(SearchType.Account, query, disposables) {
|
repoResultAccount.value = accountsRepository.getSearchData(SearchType.Account, query, disposables) {
|
||||||
it?.accounts ?: emptyList()
|
it?.accounts.orEmpty()
|
||||||
}
|
}
|
||||||
val hashtagQuery = if (query.startsWith("#")) query else "#$query"
|
val hashtagQuery = if (query.startsWith("#")) query else "#$query"
|
||||||
repoResultHashTag.value =
|
repoResultHashTag.value =
|
||||||
hashtagsRepository.getSearchData(SearchType.Hashtag, hashtagQuery, disposables) {
|
hashtagsRepository.getSearchData(SearchType.Hashtag, hashtagQuery, disposables) {
|
||||||
it?.hashtags ?: emptyList()
|
it?.hashtags.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -184,11 +179,11 @@ class SearchViewModel @Inject constructor(
|
||||||
fun bookmark(status: Pair<Status, StatusViewData.Concrete>, isBookmarked: Boolean) {
|
fun bookmark(status: Pair<Status, StatusViewData.Concrete>, isBookmarked: Boolean) {
|
||||||
val idx = loadedStatuses.indexOf(status)
|
val idx = loadedStatuses.indexOf(status)
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setFavourited(isBookmarked).createStatusViewData())
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setBookmarked(isBookmarked).createStatusViewData())
|
||||||
loadedStatuses[idx] = newPair
|
loadedStatuses[idx] = newPair
|
||||||
repoResultStatus.value?.refresh?.invoke()
|
repoResultStatus.value?.refresh?.invoke()
|
||||||
}
|
}
|
||||||
timelineCases.favourite(status.first, isBookmarked)
|
timelineCases.bookmark(status.first, isBookmarked)
|
||||||
.onErrorReturnItem(status.first)
|
.onErrorReturnItem(status.first)
|
||||||
.subscribe()
|
.subscribe()
|
||||||
.autoDispose()
|
.autoDispose()
|
||||||
|
|
|
@ -26,8 +26,7 @@ import com.keylesspalace.tusky.entity.Account
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||||
|
|
||||||
class SearchAccountsAdapter(private val linkListener: LinkListener)
|
class SearchAccountsAdapter(private val linkListener: LinkListener)
|
||||||
: PagedListAdapter<Account, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
|
: PagedListAdapter<Account, RecyclerView.ViewHolder>(ACCOUNT_COMPARATOR) {
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context)
|
val view = LayoutInflater.from(parent.context)
|
||||||
|
@ -37,21 +36,16 @@ class SearchAccountsAdapter(private val linkListener: LinkListener)
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
getItem(position)?.let { item ->
|
getItem(position)?.let { item ->
|
||||||
(holder as? AccountViewHolder)?.apply {
|
(holder as AccountViewHolder).apply {
|
||||||
setupWithAccount(item)
|
setupWithAccount(item)
|
||||||
setupLinkListener(linkListener)
|
setupLinkListener(linkListener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun getItem(position: Int): Account? {
|
|
||||||
return super.getItem(position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val STATUS_COMPARATOR = object : DiffUtil.ItemCallback<Account>() {
|
val ACCOUNT_COMPARATOR = object : DiffUtil.ItemCallback<Account>() {
|
||||||
override fun areContentsTheSame(oldItem: Account, newItem: Account): Boolean =
|
override fun areContentsTheSame(oldItem: Account, newItem: Account): Boolean =
|
||||||
oldItem.deepEquals(newItem)
|
oldItem.deepEquals(newItem)
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky.components.search.adapter
|
package com.keylesspalace.tusky.components.search.adapter
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.paging.PositionalDataSource
|
import androidx.paging.PositionalDataSource
|
||||||
import com.keylesspalace.tusky.components.search.SearchType
|
import com.keylesspalace.tusky.components.search.SearchType
|
||||||
|
@ -23,16 +22,18 @@ import com.keylesspalace.tusky.entity.SearchResult
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.util.NetworkState
|
import com.keylesspalace.tusky.util.NetworkState
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.rxkotlin.addTo
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
class SearchDataSource<T>(
|
class SearchDataSource<T>(
|
||||||
private val mastodonApi: MastodonApi,
|
private val mastodonApi: MastodonApi,
|
||||||
private val searchType: SearchType,
|
private val searchType: SearchType,
|
||||||
private val searchRequest: String?,
|
private val searchRequest: String,
|
||||||
private val disposables: CompositeDisposable,
|
private val disposables: CompositeDisposable,
|
||||||
private val retryExecutor: Executor,
|
private val retryExecutor: Executor,
|
||||||
private val initialItems: List<T>? = null,
|
private val initialItems: List<T>? = null,
|
||||||
private val parser: (SearchResult?) -> List<T>) : PositionalDataSource<T>() {
|
private val parser: (SearchResult?) -> List<T>,
|
||||||
|
private val source: SearchDataSourceFactory<T>) : PositionalDataSource<T>() {
|
||||||
|
|
||||||
val networkState = MutableLiveData<NetworkState>()
|
val networkState = MutableLiveData<NetworkState>()
|
||||||
|
|
||||||
|
@ -48,24 +49,20 @@ class SearchDataSource<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
|
||||||
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
|
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
|
||||||
if (!initialItems.isNullOrEmpty()) {
|
if (!initialItems.isNullOrEmpty()) {
|
||||||
callback.onResult(initialItems, 0)
|
callback.onResult(initialItems.toList(), 0)
|
||||||
} else {
|
} else {
|
||||||
networkState.postValue(NetworkState.LOADED)
|
networkState.postValue(NetworkState.LOADED)
|
||||||
retry = null
|
retry = null
|
||||||
initialLoad.postValue(NetworkState.LOADING)
|
initialLoad.postValue(NetworkState.LOADING)
|
||||||
mastodonApi.searchObservable(
|
mastodonApi.searchObservable(
|
||||||
query = searchRequest ?: "",
|
query = searchRequest,
|
||||||
type = searchType.apiParameter,
|
type = searchType.apiParameter,
|
||||||
resolve = true,
|
resolve = true,
|
||||||
limit = params.requestedLoadSize,
|
limit = params.requestedLoadSize,
|
||||||
offset = 0,
|
offset = 0,
|
||||||
following =false)
|
following =false)
|
||||||
.doOnSubscribe {
|
|
||||||
disposables.add(it)
|
|
||||||
}
|
|
||||||
.subscribe(
|
.subscribe(
|
||||||
{ data ->
|
{ data ->
|
||||||
val res = parser(data)
|
val res = parser(data)
|
||||||
|
@ -79,19 +76,18 @@ class SearchDataSource<T>(
|
||||||
}
|
}
|
||||||
initialLoad.postValue(NetworkState.error(error.message))
|
initialLoad.postValue(NetworkState.error(error.message))
|
||||||
}
|
}
|
||||||
)
|
).addTo(disposables)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
|
||||||
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
|
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
|
||||||
networkState.postValue(NetworkState.LOADING)
|
networkState.postValue(NetworkState.LOADING)
|
||||||
retry = null
|
retry = null
|
||||||
mastodonApi.searchObservable(searchType.apiParameter, searchRequest, true, params.loadSize, params.startPosition, false)
|
if(source.exhausted) {
|
||||||
.doOnSubscribe {
|
return callback.onResult(emptyList())
|
||||||
disposables.add(it)
|
|
||||||
}
|
}
|
||||||
|
mastodonApi.searchObservable(searchType.apiParameter, searchRequest, true, params.loadSize, params.startPosition, false)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
{ data ->
|
{ data ->
|
||||||
// Working around Mastodon bug where exact match is returned no matter
|
// Working around Mastodon bug where exact match is returned no matter
|
||||||
|
@ -105,9 +101,11 @@ class SearchDataSource<T>(
|
||||||
} else {
|
} else {
|
||||||
parser(data)
|
parser(data)
|
||||||
}
|
}
|
||||||
|
if(res.isEmpty()) {
|
||||||
|
source.exhausted = true
|
||||||
|
}
|
||||||
callback.onResult(res)
|
callback.onResult(res)
|
||||||
networkState.postValue(NetworkState.LOADED)
|
networkState.postValue(NetworkState.LOADED)
|
||||||
|
|
||||||
},
|
},
|
||||||
{ error ->
|
{ error ->
|
||||||
retry = {
|
retry = {
|
||||||
|
@ -115,7 +113,7 @@ class SearchDataSource<T>(
|
||||||
}
|
}
|
||||||
networkState.postValue(NetworkState.error(error.message))
|
networkState.postValue(NetworkState.error(error.message))
|
||||||
}
|
}
|
||||||
)
|
).addTo(disposables)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,14 +26,18 @@ import java.util.concurrent.Executor
|
||||||
class SearchDataSourceFactory<T>(
|
class SearchDataSourceFactory<T>(
|
||||||
private val mastodonApi: MastodonApi,
|
private val mastodonApi: MastodonApi,
|
||||||
private val searchType: SearchType,
|
private val searchType: SearchType,
|
||||||
private val searchRequest: String?,
|
private val searchRequest: String,
|
||||||
private val disposables: CompositeDisposable,
|
private val disposables: CompositeDisposable,
|
||||||
private val retryExecutor: Executor,
|
private val retryExecutor: Executor,
|
||||||
private val cacheData: List<T>? = null,
|
private val cacheData: List<T>? = null,
|
||||||
private val parser: (SearchResult?) -> List<T>) : DataSource.Factory<Int, T>() {
|
private val parser: (SearchResult?) -> List<T>) : DataSource.Factory<Int, T>() {
|
||||||
|
|
||||||
val sourceLiveData = MutableLiveData<SearchDataSource<T>>()
|
val sourceLiveData = MutableLiveData<SearchDataSource<T>>()
|
||||||
|
|
||||||
|
var exhausted = false
|
||||||
|
|
||||||
override fun create(): DataSource<Int, T> {
|
override fun create(): DataSource<Int, T> {
|
||||||
val source = SearchDataSource(mastodonApi, searchType, searchRequest, disposables, retryExecutor, cacheData, parser)
|
val source = SearchDataSource(mastodonApi, searchType, searchRequest, disposables, retryExecutor, cacheData, parser, this)
|
||||||
sourceLiveData.postValue(source)
|
sourceLiveData.postValue(source)
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,7 @@ import com.keylesspalace.tusky.entity.HashTag
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||||
|
|
||||||
class SearchHashtagsAdapter(private val linkListener: LinkListener)
|
class SearchHashtagsAdapter(private val linkListener: LinkListener)
|
||||||
: PagedListAdapter<HashTag, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
|
: PagedListAdapter<HashTag, RecyclerView.ViewHolder>(HASHTAG_COMPARATOR) {
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context)
|
val view = LayoutInflater.from(parent.context)
|
||||||
|
@ -36,17 +35,14 @@ class SearchHashtagsAdapter(private val linkListener: LinkListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
getItem(position)?.let { item ->
|
getItem(position)?.let { (name) ->
|
||||||
(holder as? HashtagViewHolder)?.apply {
|
(holder as HashtagViewHolder).setup(name, linkListener)
|
||||||
setup(item.name, linkListener)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val STATUS_COMPARATOR = object : DiffUtil.ItemCallback<HashTag>() {
|
val HASHTAG_COMPARATOR = object : DiffUtil.ItemCallback<HashTag>() {
|
||||||
override fun areContentsTheSame(oldItem: HashTag, newItem: HashTag): Boolean =
|
override fun areContentsTheSame(oldItem: HashTag, newItem: HashTag): Boolean =
|
||||||
oldItem.name == newItem.name
|
oldItem.name == newItem.name
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ class SearchRepository<T>(private val mastodonApi: MastodonApi) {
|
||||||
|
|
||||||
private val executor = Executors.newSingleThreadExecutor()
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
fun getSearchData(searchType: SearchType, searchRequest: String?, disposables: CompositeDisposable, pageSize: Int = 20,
|
fun getSearchData(searchType: SearchType, searchRequest: String, disposables: CompositeDisposable, pageSize: Int = 20,
|
||||||
initialItems: List<T>? = null, parser: (SearchResult?) -> List<T>): Listing<T> {
|
initialItems: List<T>? = null, parser: (SearchResult?) -> List<T>): Listing<T> {
|
||||||
val sourceFactory = SearchDataSourceFactory(mastodonApi, searchType, searchRequest, disposables, executor, initialItems, parser)
|
val sourceFactory = SearchDataSourceFactory(mastodonApi, searchType, searchRequest, disposables, executor, initialItems, parser)
|
||||||
val livePagedList = sourceFactory.toLiveData(
|
val livePagedList = sourceFactory.toLiveData(
|
||||||
|
|
|
@ -32,7 +32,6 @@ class SearchStatusesAdapter(
|
||||||
private val statusListener: StatusActionListener
|
private val statusListener: StatusActionListener
|
||||||
) : PagedListAdapter<Pair<Status, StatusViewData.Concrete>, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
|
) : PagedListAdapter<Pair<Status, StatusViewData.Concrete>, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context)
|
val view = LayoutInflater.from(parent.context)
|
||||||
.inflate(R.layout.item_status, parent, false)
|
.inflate(R.layout.item_status, parent, false)
|
||||||
|
@ -41,8 +40,7 @@ class SearchStatusesAdapter(
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
getItem(position)?.let { item ->
|
getItem(position)?.let { item ->
|
||||||
(holder as? StatusViewHolder)?.setupWithStatus(item.second, statusListener,
|
(holder as StatusViewHolder).setupWithStatus(item.second, statusListener, statusDisplayOptions)
|
||||||
statusDisplayOptions)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import androidx.paging.PagedList
|
||||||
import androidx.paging.PagedListAdapter
|
import androidx.paging.PagedListAdapter
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.keylesspalace.tusky.AccountActivity
|
import com.keylesspalace.tusky.AccountActivity
|
||||||
|
@ -62,8 +63,8 @@ abstract class SearchFragment<T> : Fragment(),
|
||||||
swipeRefreshLayout.setOnRefreshListener(this)
|
swipeRefreshLayout.setOnRefreshListener(this)
|
||||||
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||||
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(
|
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(
|
||||||
ThemeUtils.getColor(swipeRefreshLayout.context, android.R.attr.colorBackground))
|
ThemeUtils.getColor(swipeRefreshLayout.context, android.R.attr.colorBackground)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun subscribeObservables() {
|
private fun subscribeObservables() {
|
||||||
|
@ -75,8 +76,9 @@ abstract class SearchFragment<T> : Fragment(),
|
||||||
|
|
||||||
searchProgressBar.visible(it == NetworkState.LOADING)
|
searchProgressBar.visible(it == NetworkState.LOADING)
|
||||||
|
|
||||||
if (it.status == Status.FAILED)
|
if (it.status == Status.FAILED) {
|
||||||
showError(it.msg)
|
showError()
|
||||||
|
}
|
||||||
checkNoData()
|
checkNoData()
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -85,8 +87,9 @@ abstract class SearchFragment<T> : Fragment(),
|
||||||
|
|
||||||
progressBarBottom.visible(it == NetworkState.LOADING)
|
progressBarBottom.visible(it == NetworkState.LOADING)
|
||||||
|
|
||||||
if (it.status == Status.FAILED)
|
if (it.status == Status.FAILED) {
|
||||||
showError(it.msg)
|
showError()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +102,8 @@ abstract class SearchFragment<T> : Fragment(),
|
||||||
searchRecyclerView.layoutManager = LinearLayoutManager(searchRecyclerView.context)
|
searchRecyclerView.layoutManager = LinearLayoutManager(searchRecyclerView.context)
|
||||||
adapter = createAdapter()
|
adapter = createAdapter()
|
||||||
searchRecyclerView.adapter = adapter
|
searchRecyclerView.adapter = adapter
|
||||||
|
searchRecyclerView.setHasFixedSize(true)
|
||||||
|
(searchRecyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNoData(isEmpty: Boolean) {
|
private fun showNoData(isEmpty: Boolean) {
|
||||||
|
@ -109,7 +113,7 @@ abstract class SearchFragment<T> : Fragment(),
|
||||||
searchNoResultsText.hide()
|
searchNoResultsText.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showError(@Suppress("UNUSED_PARAMETER") msg: String?) {
|
private fun showError() {
|
||||||
if (snackbarErrorRetry?.isShown != true) {
|
if (snackbarErrorRetry?.isShown != true) {
|
||||||
snackbarErrorRetry = Snackbar.make(layoutRoot, R.string.failed_search, Snackbar.LENGTH_INDEFINITE)
|
snackbarErrorRetry = Snackbar.make(layoutRoot, R.string.failed_search, Snackbar.LENGTH_INDEFINITE)
|
||||||
snackbarErrorRetry?.setAction(R.string.action_retry) {
|
snackbarErrorRetry?.setAction(R.string.action_retry) {
|
||||||
|
@ -135,7 +139,6 @@ abstract class SearchFragment<T> : Fragment(),
|
||||||
|
|
||||||
// Dismissed here because the RecyclerView bottomProgressBar is shown as soon as the retry begins.
|
// Dismissed here because the RecyclerView bottomProgressBar is shown as soon as the retry begins.
|
||||||
swipeRefreshLayout.post {
|
swipeRefreshLayout.post {
|
||||||
|
|
||||||
swipeRefreshLayout.isRefreshing = false
|
swipeRefreshLayout.isRefreshing = false
|
||||||
}
|
}
|
||||||
viewModel.retryAllSearches()
|
viewModel.retryAllSearches()
|
||||||
|
|
|
@ -59,7 +59,6 @@ import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
|
||||||
import com.uber.autodispose.autoDispose
|
import com.uber.autodispose.autoDispose
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.synthetic.main.fragment_search.*
|
import kotlinx.android.synthetic.main.fragment_search.*
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concrete>>(), StatusActionListener {
|
class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concrete>>(), StatusActionListener {
|
||||||
|
|
||||||
|
@ -70,6 +69,9 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
override val data: LiveData<PagedList<Pair<Status, StatusViewData.Concrete>>>
|
override val data: LiveData<PagedList<Pair<Status, StatusViewData.Concrete>>>
|
||||||
get() = viewModel.statuses
|
get() = viewModel.statuses
|
||||||
|
|
||||||
|
private val searchAdapter
|
||||||
|
get() = super.adapter as SearchStatusesAdapter
|
||||||
|
|
||||||
override fun createAdapter(): PagedListAdapter<Pair<Status, StatusViewData.Concrete>, *> {
|
override fun createAdapter(): PagedListAdapter<Pair<Status, StatusViewData.Concrete>, *> {
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context)
|
||||||
val statusDisplayOptions = StatusDisplayOptions(
|
val statusDisplayOptions = StatusDisplayOptions(
|
||||||
|
@ -87,37 +89,37 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
|
|
||||||
|
|
||||||
override fun onContentHiddenChange(isShowing: Boolean, position: Int) {
|
override fun onContentHiddenChange(isShowing: Boolean, position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let {
|
searchAdapter.getItem(position)?.let {
|
||||||
viewModel.contentHiddenChange(it, isShowing)
|
viewModel.contentHiddenChange(it, isShowing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReply(position: Int) {
|
override fun onReply(position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let { status ->
|
searchAdapter.getItem(position)?.first?.let { status ->
|
||||||
reply(status)
|
reply(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFavourite(favourite: Boolean, position: Int) {
|
override fun onFavourite(favourite: Boolean, position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let { status ->
|
searchAdapter.getItem(position)?.let { status ->
|
||||||
viewModel.favorite(status, favourite)
|
viewModel.favorite(status, favourite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBookmark(bookmark: Boolean, position: Int) {
|
override fun onBookmark(bookmark: Boolean, position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let { status ->
|
searchAdapter.getItem(position)?.let { status ->
|
||||||
viewModel.bookmark(status, bookmark)
|
viewModel.bookmark(status, bookmark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMore(view: View, position: Int) {
|
override fun onMore(view: View, position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let {
|
searchAdapter.getItem(position)?.first?.let {
|
||||||
more(it, view, position)
|
more(it, view, position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) {
|
override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.actionableStatus?.let { actionable ->
|
searchAdapter.getItem(position)?.first?.actionableStatus?.let { actionable ->
|
||||||
when (actionable.attachments[attachmentIndex].type) {
|
when (actionable.attachments[attachmentIndex].type) {
|
||||||
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
|
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
|
||||||
val attachments = AttachmentViewData.list(actionable)
|
val attachments = AttachmentViewData.list(actionable)
|
||||||
|
@ -142,48 +144,48 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewThread(position: Int) {
|
override fun onViewThread(position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let { status ->
|
searchAdapter.getItem(position)?.first?.let { status ->
|
||||||
val actionableStatus = status.actionableStatus
|
val actionableStatus = status.actionableStatus
|
||||||
bottomSheetActivity?.viewThread(actionableStatus.id, actionableStatus.url)
|
bottomSheetActivity?.viewThread(actionableStatus.id, actionableStatus.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenReblog(position: Int) {
|
override fun onOpenReblog(position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let { status ->
|
searchAdapter.getItem(position)?.first?.let { status ->
|
||||||
bottomSheetActivity?.viewAccount(status.account.id)
|
bottomSheetActivity?.viewAccount(status.account.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onExpandedChange(expanded: Boolean, position: Int) {
|
override fun onExpandedChange(expanded: Boolean, position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let {
|
searchAdapter.getItem(position)?.let {
|
||||||
viewModel.expandedChange(it, expanded)
|
viewModel.expandedChange(it, expanded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadMore(position: Int) {
|
override fun onLoadMore(position: Int) {
|
||||||
//Ignore
|
// Not possible here
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onContentCollapsedChange(isCollapsed: Boolean, position: Int) {
|
override fun onContentCollapsedChange(isCollapsed: Boolean, position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let {
|
searchAdapter.getItem(position)?.let {
|
||||||
viewModel.collapsedChange(it, isCollapsed)
|
viewModel.collapsedChange(it, isCollapsed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVoteInPoll(position: Int, choices: MutableList<Int>) {
|
override fun onVoteInPoll(position: Int, choices: MutableList<Int>) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let {
|
searchAdapter.getItem(position)?.let {
|
||||||
viewModel.voteInPoll(it, choices)
|
viewModel.voteInPoll(it, choices)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeItem(position: Int) {
|
private fun removeItem(position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let {
|
searchAdapter.getItem(position)?.let {
|
||||||
viewModel.removeItem(it)
|
viewModel.removeItem(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReblog(reblog: Boolean, position: Int) {
|
override fun onReblog(reblog: Boolean, position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let { status ->
|
searchAdapter.getItem(position)?.let { status ->
|
||||||
viewModel.reblog(status, reblog)
|
viewModel.reblog(status, reblog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,27 +195,23 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reply(status: Status) {
|
private fun reply(status: Status) {
|
||||||
val inReplyToId = status.actionableId
|
|
||||||
val actionableStatus = status.actionableStatus
|
val actionableStatus = status.actionableStatus
|
||||||
val replyVisibility = actionableStatus.visibility
|
val mentionedUsernames = actionableStatus.mentions.map { it.username }
|
||||||
val contentWarning = actionableStatus.spoilerText
|
.toMutableSet()
|
||||||
val mentions = actionableStatus.mentions
|
.apply {
|
||||||
val mentionedUsernames = LinkedHashSet<String>()
|
add(actionableStatus.account.username)
|
||||||
mentionedUsernames.add(actionableStatus.account.username)
|
remove(viewModel.activeAccount?.username)
|
||||||
val loggedInUsername = viewModel.activeAccount?.username
|
|
||||||
for ((_, _, username) in mentions) {
|
|
||||||
mentionedUsernames.add(username)
|
|
||||||
}
|
}
|
||||||
mentionedUsernames.remove(loggedInUsername)
|
|
||||||
val intent = ComposeActivity.startIntent(context!!, ComposeOptions(
|
val intent = ComposeActivity.startIntent(requireContext(), ComposeOptions(
|
||||||
inReplyToId = inReplyToId,
|
inReplyToId = status.actionableId,
|
||||||
replyVisibility = replyVisibility,
|
replyVisibility = actionableStatus.visibility,
|
||||||
contentWarning = contentWarning,
|
contentWarning = actionableStatus.spoilerText,
|
||||||
mentionedUsernames = mentionedUsernames,
|
mentionedUsernames = mentionedUsernames,
|
||||||
replyingStatusAuthor = actionableStatus.account.localUsername,
|
replyingStatusAuthor = actionableStatus.account.localUsername,
|
||||||
replyingStatusContent = actionableStatus.content.toString()
|
replyingStatusContent = actionableStatus.content.toString()
|
||||||
))
|
))
|
||||||
requireActivity().startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun more(status: Status, view: View, position: Int) {
|
private fun more(status: Status, view: View, position: Int) {
|
||||||
|
@ -252,8 +250,7 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val menu = popup.menu
|
val openAsItem = popup.menu.findItem(R.id.status_open_as)
|
||||||
val openAsItem = menu.findItem(R.id.status_open_as)
|
|
||||||
when (accounts.size) {
|
when (accounts.size) {
|
||||||
0, 1 -> openAsItem.isVisible = false
|
0, 1 -> openAsItem.isVisible = false
|
||||||
2 -> for (account in accounts) {
|
2 -> for (account in accounts) {
|
||||||
|
@ -269,13 +266,12 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
popup.setOnMenuItemClickListener { item ->
|
popup.setOnMenuItemClickListener { item ->
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.status_share_content -> {
|
R.id.status_share_content -> {
|
||||||
var statusToShare: Status? = status
|
val statusToShare: Status = status.actionableStatus
|
||||||
if (statusToShare!!.reblog != null) statusToShare = statusToShare.reblog
|
|
||||||
|
|
||||||
val sendIntent = Intent()
|
val sendIntent = Intent()
|
||||||
sendIntent.action = Intent.ACTION_SEND
|
sendIntent.action = Intent.ACTION_SEND
|
||||||
|
|
||||||
val stringToShare = statusToShare!!.account.username +
|
val stringToShare = statusToShare.account.username +
|
||||||
" - " +
|
" - " +
|
||||||
statusToShare.content.toString()
|
statusToShare.content.toString()
|
||||||
sendIntent.putExtra(Intent.EXTRA_TEXT, stringToShare)
|
sendIntent.putExtra(Intent.EXTRA_TEXT, stringToShare)
|
||||||
|
@ -292,7 +288,7 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
return@setOnMenuItemClickListener true
|
return@setOnMenuItemClickListener true
|
||||||
}
|
}
|
||||||
R.id.status_copy_link -> {
|
R.id.status_copy_link -> {
|
||||||
val clipboard = activity!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
clipboard.setPrimaryClip(ClipData.newPlainText(null, statusUrl))
|
clipboard.setPrimaryClip(ClipData.newPlainText(null, statusUrl))
|
||||||
return@setOnMenuItemClickListener true
|
return@setOnMenuItemClickListener true
|
||||||
}
|
}
|
||||||
|
@ -365,7 +361,7 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
val uri = Uri.parse(url)
|
val uri = Uri.parse(url)
|
||||||
val filename = uri.lastPathSegment
|
val filename = uri.lastPathSegment
|
||||||
|
|
||||||
val downloadManager = activity!!.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
val downloadManager = requireActivity().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||||
val request = DownloadManager.Request(uri)
|
val request = DownloadManager.Request(uri)
|
||||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename)
|
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename)
|
||||||
downloadManager.enqueue(request)
|
downloadManager.enqueue(request)
|
||||||
|
@ -417,7 +413,7 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
deletedStatus
|
deletedStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
val intent = ComposeActivity.startIntent(context!!, ComposeOptions(
|
val intent = ComposeActivity.startIntent(requireContext(), ComposeOptions(
|
||||||
tootText = redraftStatus.text ?: "",
|
tootText = redraftStatus.text ?: "",
|
||||||
inReplyToId = redraftStatus.inReplyToId,
|
inReplyToId = redraftStatus.inReplyToId,
|
||||||
visibility = redraftStatus.visibility,
|
visibility = redraftStatus.visibility,
|
||||||
|
|
Loading…
Reference in New Issue