Skip to content

Commit

Permalink
Use paging library in SearchFragment.
Browse files Browse the repository at this point in the history
  • Loading branch information
Isira-Seneviratne committed Aug 7, 2022
1 parent cda7739 commit 6ca604c
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I

public static SearchFragment getInstance(final int serviceId, final String searchString) {
final SearchFragment searchFragment = new SearchFragment();
searchFragment.setQuery(serviceId, searchString, new String[0], "");
searchFragment.setQuery(serviceId, searchString, new String[0]);

if (!TextUtils.isEmpty(searchString)) {
searchFragment.setSearchOnResume();
Expand Down Expand Up @@ -265,11 +265,11 @@ public void onResume() {

if (!TextUtils.isEmpty(searchString)) {
if (wasLoading.getAndSet(false)) {
search(searchString, contentFilter, sortFilter);
search(searchString);
return;
} else if (infoListAdapter.getItemsList().isEmpty()) {
if (savedState == null) {
search(searchString, contentFilter, sortFilter);
search(searchString);
return;
} else if (!isLoading.get() && !wasSearchFocused && lastPanelError == null) {
infoListAdapter.clearStreamItemList();
Expand Down Expand Up @@ -322,7 +322,7 @@ public void onActivityResult(final int requestCode, final int resultCode, final
if (requestCode == ReCaptchaActivity.RECAPTCHA_REQUEST) {
if (resultCode == Activity.RESULT_OK
&& !TextUtils.isEmpty(searchString)) {
search(searchString, contentFilter, sortFilter);
search(searchString);
} else {
Log.e(TAG, "ReCaptcha failed");
}
Expand Down Expand Up @@ -399,7 +399,7 @@ public void reloadContent() {
|| (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
search(!TextUtils.isEmpty(searchString)
? searchString
: searchEditText.getText().toString(), this.contentFilter, "");
: searchEditText.getText().toString());
} else {
if (searchEditText != null) {
searchEditText.setText("");
Expand Down Expand Up @@ -562,19 +562,19 @@ private void initSearchListeners() {
suggestionListAdapter.setListener(new SuggestionListAdapter.OnSuggestionItemSelected() {
@Override
public void onSuggestionItemSelected(final SuggestionItem item) {
search(item.query, new String[0], "");
searchEditText.setText(item.query);
search(item.getQuery());
searchEditText.setText(item.getQuery());
}

@Override
public void onSuggestionItemInserted(final SuggestionItem item) {
searchEditText.setText(item.query);
searchEditText.setText(item.getQuery());
searchEditText.setSelection(searchEditText.getText().length());
}

@Override
public void onSuggestionItemLongClick(final SuggestionItem item) {
if (item.fromHistory) {
if (item.isFromHistory()) {
showDeleteSuggestionDialog(item);
}
}
Expand Down Expand Up @@ -617,7 +617,7 @@ public void afterTextChanged(final Editable s) {
} else if (event != null
&& (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
|| event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
search(searchEditText.getText().toString(), new String[0], "");
search(searchEditText.getText().toString());
return true;
}
return false;
Expand Down Expand Up @@ -681,7 +681,7 @@ private void showDeleteSuggestionDialog(final SuggestionItem item) {
if (activity == null || historyRecordManager == null || searchEditText == null) {
return;
}
final String query = item.query;
final String query = item.getQuery();
new AlertDialog.Builder(activity)
.setTitle(query)
.setMessage(R.string.delete_item_search_history)
Expand Down Expand Up @@ -774,7 +774,7 @@ private void initSuggestionObserver() {
return getRemoteSuggestionsObservable(query)
.materialize();
} else {
return Single.fromCallable(Collections::<SuggestionItem>emptyList)
return Single.just(Collections.<SuggestionItem>emptyList())
.toObservable()
.materialize();
}
Expand Down Expand Up @@ -803,9 +803,7 @@ protected void doInitialLoadLogic() {
// no-op
}

private void search(final String theSearchString,
final String[] theContentFilter,
final String theSortFilter) {
private void search(final String theSearchString) {
if (DEBUG) {
Log.d(TAG, "search() called with: query = [" + theSearchString + "]");
}
Expand Down Expand Up @@ -922,18 +920,16 @@ private void changeContentFilter(final MenuItem item, final List<String> theCont
contentFilter = theContentFilter.toArray(new String[0]);

if (!TextUtils.isEmpty(searchString)) {
search(searchString, contentFilter, sortFilter);
search(searchString);
}
}

private void setQuery(final int theServiceId,
final String theSearchString,
final String[] theContentFilter,
final String theSortFilter) {
private void setQuery(final int theServiceId, final String theSearchString,
final String[] theContentFilter) {
serviceId = theServiceId;
searchString = theSearchString;
contentFilter = theContentFilter;
sortFilter = theSortFilter;
sortFilter = "";
}

/*//////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1018,7 +1014,7 @@ private void handleSearchSuggestion() {

searchBinding.correctSuggestion.setOnClickListener(v -> {
searchBinding.correctSuggestion.setVisibility(View.GONE);
search(searchSuggestion, contentFilter, sortFilter);
search(searchSuggestion);
searchEditText.setText(searchSuggestion);
});

Expand Down Expand Up @@ -1067,13 +1063,13 @@ public int getSuggestionMovementFlags(@NonNull final RecyclerView.ViewHolder vie
}

final SuggestionItem item = suggestionListAdapter.getItem(position);
return item.fromHistory ? makeMovementFlags(0,
return item.isFromHistory() ? makeMovementFlags(0,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
}

public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder) {
final int position = viewHolder.getBindingAdapterPosition();
final String query = suggestionListAdapter.getItem(position).query;
final String query = suggestionListAdapter.getItem(position).getQuery();
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.schabi.newpipe.fragments.list.search

import androidx.paging.PagingSource
import androidx.paging.PagingState
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.search.SearchInfo
import org.schabi.newpipe.util.ExtractorHelper

class SearchPagingSource(
private val serviceId: Int,
private val query: String,
private val contentFilter: List<String>,
private val sortFilter: String,
) : PagingSource<Page, SearchInfo>() {
override fun getRefreshKey(state: PagingState<Page, SearchInfo>): Page? {
TODO("Not yet implemented")
}

override suspend fun load(params: LoadParams<Page>): LoadResult<Page, SearchInfo> {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.schabi.newpipe.fragments.list.search

import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.search.SearchInfo

@OptIn(ExperimentalPagingApi::class)
class SearchRemoteMediator(
private val serviceId: Int,
private val query: String,
private val contentFilter: List<String>,
private val sortFilter: String
) : RemoteMediator<Page, SearchInfo>() {
override suspend fun load(
loadType: LoadType,
state: PagingState<Page, SearchInfo>
): MediatorResult {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.schabi.newpipe.fragments.list.search

import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow
import org.schabi.newpipe.extractor.search.SearchInfo

fun getSearchResultStream(
serviceId: Int, query: String, contentFilter: List<String>, sortFilter: String
): Flow<PagingData<SearchInfo>> {
return Pager(
config = PagingConfig(pageSize = 50, enablePlaceholders = false),
pagingSourceFactory = { SearchPagingSource(serviceId, query, contentFilter, sortFilter) }
).flow
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package org.schabi.newpipe.fragments.list.search

import android.app.Application
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.subjects.PublishSubject
import org.schabi.newpipe.App
import org.schabi.newpipe.local.history.HistoryRecordManager
import org.schabi.newpipe.util.ExtractorHelper
import java.util.concurrent.TimeUnit

class SearchViewModel(
application: Application,
private val serviceId: Int,
private val showLocalSuggestions: Boolean,
private val showRemoteSuggestions: Boolean
) : ViewModel() {
private val historyRecordManager = HistoryRecordManager(application)
private val suggestionPublisher = PublishSubject.create<String>()

private val suggestionMutableLiveData = MutableLiveData<SuggestionItemResponse>()
val suggestionLiveData: LiveData<SuggestionItemResponse> get() = suggestionMutableLiveData

private val suggestionDisposable = suggestionPublisher
.debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS)
.switchMap { query: String ->
// Only show remote suggestions if they are enabled in settings and
// the query length is at least THRESHOLD_NETWORK_SUGGESTION
val shallShowRemoteSuggestionsNow = (showRemoteSuggestions && query.length >= THRESHOLD_NETWORK_SUGGESTION)
if (showLocalSuggestions && shallShowRemoteSuggestionsNow) {
Observable.zip(
getLocalSuggestionsObservable(query, 3),
getRemoteSuggestionsObservable(query)
) { local, remote -> (local + remote).distinct() }
} else if (showLocalSuggestions) {
getLocalSuggestionsObservable(query, 25)
} else if (shallShowRemoteSuggestionsNow) {
getRemoteSuggestionsObservable(query)
} else {
Single.just(emptyList<SuggestionItem>()).toObservable()
}
}
.subscribe({ suggestions ->
suggestions.forEach { suggestionMutableLiveData.postValue(SuggestionItemSuccess(it)) }
}) {
suggestionMutableLiveData.postValue(SuggestionItemError(it))
}

override fun onCleared() {
suggestionDisposable.dispose()
}

fun updateSearchQuery(query: String) {
suggestionPublisher.onNext(query)
}

private fun getLocalSuggestionsObservable(query: String, similarQueryLimit: Int): Observable<List<SuggestionItem>> {
return historyRecordManager.getRelatedSearches(query, similarQueryLimit, 25)
.toObservable()
.map { entries -> entries.map { SuggestionItem(true, it) } }
}

private fun getRemoteSuggestionsObservable(query: String): Observable<List<SuggestionItem>> {
return ExtractorHelper.suggestionsFor(serviceId, query)
.toObservable()
.map { entries -> entries.map { SuggestionItem(false, it) } }
}

companion object {
/**
* How much time have to pass without emitting a item (i.e. the user stop typing)
* to fetch/show the suggestions, in milliseconds.
*/
private const val SUGGESTIONS_DEBOUNCE = 120L // ms

/**
* The suggestions will only be fetched from network if the query meet this threshold (>=).
* (local ones will be fetched regardless of the length)
*/
private const val THRESHOLD_NETWORK_SUGGESTION = 1

fun getFactory(
serviceId: Int,
showLocalSuggestions: Boolean,
showRemoteSuggestions: Boolean
) = viewModelFactory {
initializer {
SearchViewModel(App.getApp(), serviceId, showLocalSuggestions, showRemoteSuggestions)
}
}
}
}

sealed class SuggestionItemResponse
class SuggestionItemSuccess(val item: SuggestionItem) : SuggestionItemResponse()
class SuggestionItemError(val throwable: Throwable) : SuggestionItemResponse()
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@
import androidx.annotation.NonNull;

public class SuggestionItem {
final boolean fromHistory;
public final String query;
private final boolean fromHistory;
private final String query;

public SuggestionItem(final boolean fromHistory, final String query) {
this.fromHistory = fromHistory;
this.query = query;
}

public boolean isFromHistory() {
return fromHistory;
}

public String getQuery() {
return query;
}

@Override
public boolean equals(final Object o) {
if (o instanceof SuggestionItem) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ private SuggestionItemHolder(final View rootView) {
}

private void updateFrom(final SuggestionItem item) {
suggestionIcon.setImageResource(item.fromHistory ? historyResId : searchResId);
itemSuggestionQuery.setText(item.query);
suggestionIcon.setImageResource(item.isFromHistory() ? historyResId : searchResId);
itemSuggestionQuery.setText(item.getQuery());
}
}
}

0 comments on commit 6ca604c

Please sign in to comment.