Skip to content

Commit e8c3892

Browse files
committed
Display fragment based on search text
Keep Main Fragment until user's input
1 parent 276bf39 commit e8c3892

5 files changed

Lines changed: 165 additions & 37 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -764,14 +764,14 @@ private void initSuggestionObserver() {
764764

765765
if (showLocalSuggestions && shallShowRemoteSuggestionsNow) {
766766
return Observable.zip(
767-
getLocalSuggestionsObservable(query, 3),
768-
getRemoteSuggestionsObservable(query),
769-
(local, remote) -> {
770-
remote.removeIf(remoteItem -> local.stream().anyMatch(
771-
localItem -> localItem.equals(remoteItem)));
772-
local.addAll(remote);
773-
return local;
774-
})
767+
getLocalSuggestionsObservable(query, 3),
768+
getRemoteSuggestionsObservable(query),
769+
(local, remote) -> {
770+
remote.removeIf(remoteItem -> local.stream().anyMatch(
771+
localItem -> localItem.equals(remoteItem)));
772+
local.addAll(remote);
773+
return local;
774+
})
775775
.materialize();
776776
} else if (showLocalSuggestions) {
777777
return getLocalSuggestionsObservable(query, 25)
@@ -876,9 +876,9 @@ public void startLoading(final boolean forceLoad) {
876876
searchDisposable.dispose();
877877
}
878878
searchDisposable = ExtractorHelper.searchFor(serviceId,
879-
searchString,
880-
Arrays.asList(contentFilter),
881-
sortFilter)
879+
searchString,
880+
Arrays.asList(contentFilter),
881+
sortFilter)
882882
.subscribeOn(Schedulers.io())
883883
.observeOn(AndroidSchedulers.mainThread())
884884
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
@@ -897,11 +897,11 @@ protected void loadMoreItems() {
897897
searchDisposable.dispose();
898898
}
899899
searchDisposable = ExtractorHelper.getMoreSearchItems(
900-
serviceId,
901-
searchString,
902-
asList(contentFilter),
903-
sortFilter,
904-
nextPage)
900+
serviceId,
901+
searchString,
902+
asList(contentFilter),
903+
sortFilter,
904+
nextPage)
905905
.subscribeOn(Schedulers.io())
906906
.observeOn(AndroidSchedulers.mainThread())
907907
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))

app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import androidx.preference.PreferenceFragmentCompat;
2323

2424
import com.evernote.android.state.State;
25-
import com.jakewharton.rxbinding4.widget.RxTextView;
2625
import com.livefront.bridge.Bridge;
2726

2827
import org.schabi.newpipe.MainActivity;
@@ -43,6 +42,10 @@
4342

4443
import java.util.concurrent.TimeUnit;
4544

45+
import io.reactivex.rxjava3.disposables.Disposable;
46+
47+
import com.jakewharton.rxbinding4.widget.RxTextView;
48+
4649
/*
4750
* Created by Christian Schabesberger on 31.08.15.
4851
*
@@ -86,6 +89,8 @@ public class SettingsActivity extends AppCompatActivity implements
8689
@State
8790
boolean wasSearchActive;
8891

92+
private Disposable searchDisposable;
93+
8994
@Override
9095
protected void onCreate(final Bundle savedInstanceBundle) {
9196
setTheme(ThemeHelper.getSettingsThemeStyle(this));
@@ -111,9 +116,7 @@ protected void onCreate(final Bundle savedInstanceBundle) {
111116
}
112117
}
113118
} else {
114-
getSupportFragmentManager().beginTransaction()
115-
.replace(R.id.settings_fragment_holder, new MainSettingsFragment())
116-
.commit();
119+
showMainSettingsFragment();
117120
}
118121

119122
if (DeviceUtils.isTv(this)) {
@@ -189,8 +192,18 @@ private void showSettingsFragment(final Fragment fragment) {
189192
.commit();
190193
}
191194

195+
private void showMainSettingsFragment() {
196+
getSupportFragmentManager().beginTransaction()
197+
.replace(R.id.settings_fragment_holder, new MainSettingsFragment())
198+
.commit();
199+
}
200+
201+
192202
@Override
193203
protected void onDestroy() {
204+
if (searchDisposable != null && !searchDisposable.isDisposed()) {
205+
searchDisposable.dispose();
206+
}
194207
setMenuSearchItem(null);
195208
searchFragment = null;
196209
super.onDestroy();
@@ -205,16 +218,43 @@ private void initSearch(
205218
final SettingsLayoutBinding settingsLayoutBinding,
206219
final boolean restored
207220
) {
221+
208222
searchContainer =
209223
settingsLayoutBinding.settingsToolbarLayout.toolbar
210224
.findViewById(R.id.toolbar_search_container);
211225

212226
// Configure input field for search
213227
searchEditText = searchContainer.findViewById(R.id.toolbar_search_edit_text);
214-
RxTextView.textChanges(searchEditText)
228+
searchDisposable = RxTextView.textChanges(searchEditText)
229+
// Ignore the initial empty state
230+
.skipInitialValue()
215231
// Wait some time after the last input before actually searching
216232
.debounce(200, TimeUnit.MILLISECONDS)
217-
.subscribe(v -> runOnUiThread(this::onSearchChanged));
233+
.subscribe(charSequence -> runOnUiThread(() -> {
234+
// Change Fragment based on search text
235+
if (charSequence.length() > 0) {
236+
if (searchFragment != null) {
237+
if (!searchFragment.isAdded()) {
238+
getSupportFragmentManager().beginTransaction()
239+
.replace(R.id.settings_fragment_holder, searchFragment,
240+
PreferenceSearchFragment.NAME)
241+
.addToBackStack(PreferenceSearchFragment.NAME)
242+
.commit();
243+
} else {
244+
showSettingsFragment(searchFragment);
245+
}
246+
onSearchChanged();
247+
}
248+
} else {
249+
final Fragment current = getSupportFragmentManager()
250+
.findFragmentById(R.id.settings_fragment_holder);
251+
if (!(current instanceof MainSettingsFragment)) {
252+
getSupportFragmentManager()
253+
.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
254+
showMainSettingsFragment();
255+
}
256+
}
257+
}));
218258

219259
// Configure clear button
220260
searchContainer.findViewById(R.id.toolbar_search_clear)
@@ -294,25 +334,36 @@ public void setSearchActive(final boolean active) {
294334
Log.d(TAG, "setSearchActive called active=" + active);
295335
}
296336

337+
// Only reset the text when activating search from the toolbar
338+
if (active && !isSearchActive()) {
339+
resetSearchText();
340+
}
341+
297342
// Ignore if search is already in correct state
298343
if (isSearchActive() == active) {
299344
return;
300345
}
301346

302347
wasSearchActive = active;
348+
if (wasSearchActive) {
349+
searchEditText.post(() -> {
350+
searchEditText.requestFocus();
351+
KeyboardUtil.showKeyboard(this, searchEditText);
352+
});
353+
}
303354

304355
searchContainer.setVisibility(active ? View.VISIBLE : View.GONE);
305356
if (menuSearchItem != null) {
306357
menuSearchItem.setVisible(!active);
307358
}
308359

309-
if (active) {
310-
getSupportFragmentManager()
311-
.beginTransaction()
312-
.add(FRAGMENT_HOLDER_ID, searchFragment, PreferenceSearchFragment.NAME)
313-
.addToBackStack(PreferenceSearchFragment.NAME)
314-
.commit();
315-
360+
if (active && searchText == null) {
361+
showMainSettingsFragment();
362+
} else if (active) {
363+
// Only add if not already added
364+
if (!searchFragment.isAdded()) {
365+
showSettingsFragment(searchFragment);
366+
}
316367
KeyboardUtil.showKeyboard(this, searchEditText);
317368
} else if (searchFragment != null) {
318369
hideSearchFragment();
@@ -323,8 +374,6 @@ public void setSearchActive(final boolean active) {
323374

324375
KeyboardUtil.hideKeyboard(this, searchEditText);
325376
}
326-
327-
resetSearchText();
328377
}
329378

330379
private void hideSearchFragment() {
@@ -388,5 +437,18 @@ public void onSearchResultClicked(@NonNull final PreferenceSearchItem result) {
388437
}
389438
}
390439

440+
// Functions needed for testing
441+
protected EditText getSearchEditText() {
442+
return searchEditText;
443+
}
444+
445+
public void setMockEditText(final EditText editText) {
446+
this.searchEditText = editText;
447+
}
448+
449+
protected static int getFragmentHolderId() {
450+
return FRAGMENT_HOLDER_ID;
451+
}
452+
391453
//endregion
392454
}

app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package org.schabi.newpipe.settings.preferencesearch;
22

3-
import android.text.TextUtils;
4-
53
import java.util.ArrayList;
6-
import java.util.Collections;
74
import java.util.List;
85
import java.util.stream.Collectors;
96

@@ -21,9 +18,6 @@ public void add(final List<PreferenceSearchItem> items) {
2118
}
2219

2320
List<PreferenceSearchItem> searchFor(final String keyword) {
24-
if (TextUtils.isEmpty(keyword)) {
25-
return Collections.emptyList();
26-
}
2721

2822
return configuration.getSearcher()
2923
.search(allEntries.stream(), keyword)

app/src/main/res/layout/settings_preferencesearch_fragment.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
android:layout_height="match_parent"
1919
android:gravity="center"
2020
android:orientation="vertical"
21+
android:clickable="true"
2122
android:visibility="gone"
2223
tools:visibility="gone">
2324

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.schabi.newpipe.settings;
2+
3+
import android.widget.EditText;
4+
5+
import androidx.fragment.app.Fragment;
6+
import androidx.fragment.app.FragmentManager;
7+
8+
import org.junit.Before;
9+
import org.junit.Test;
10+
import org.mockito.Mockito;
11+
import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchFragment;
12+
13+
import static org.junit.Assert.assertTrue;
14+
import static org.mockito.ArgumentMatchers.anyInt;
15+
import static org.mockito.Mockito.when;
16+
17+
public class SettingsActivityTest {
18+
19+
private SettingsActivity activity;
20+
private FragmentManager fragmentManager;
21+
22+
@Before
23+
public void setUp() {
24+
activity = Mockito.spy(new SettingsActivity());
25+
fragmentManager = Mockito.mock(FragmentManager.class);
26+
Mockito.doReturn(fragmentManager).when(activity).getSupportFragmentManager();
27+
// Mock the searchEditText
28+
final EditText editText = Mockito.mock(EditText.class);
29+
activity.setMockEditText(editText);
30+
}
31+
32+
@Test
33+
public void mainSettingsFragmentDisplayedWhenSearchInactive() {
34+
when(fragmentManager.findFragmentById(anyInt()))
35+
.thenReturn(new MainSettingsFragment());
36+
37+
activity.setSearchActive(false);
38+
39+
final Fragment fragment = activity.getSupportFragmentManager()
40+
.findFragmentById(SettingsActivity.getFragmentHolderId());
41+
assertTrue(fragment instanceof MainSettingsFragment);
42+
}
43+
44+
@Test
45+
public void preferenceSearchFragmentDisplayedWhenSearchActiveAndTextNotEmpty() {
46+
when(fragmentManager.findFragmentById(anyInt()))
47+
.thenReturn(new PreferenceSearchFragment());
48+
49+
activity.setSearchActive(true);
50+
Mockito.when(activity.getSearchEditText().getText())
51+
.thenReturn(new android.text.SpannableStringBuilder("query"));
52+
53+
final Fragment fragment = activity.getSupportFragmentManager()
54+
.findFragmentById(SettingsActivity.getFragmentHolderId());
55+
assertTrue(fragment instanceof PreferenceSearchFragment);
56+
}
57+
58+
@Test
59+
public void mainSettingsFragmentDisplayedWhenSearchActiveButTextEmpty() {
60+
when(fragmentManager.findFragmentById(anyInt()))
61+
.thenReturn(new MainSettingsFragment());
62+
63+
activity.setSearchActive(true);
64+
Mockito.when(activity.getSearchEditText().getText())
65+
.thenReturn(new android.text.SpannableStringBuilder(""));
66+
67+
final Fragment fragment = activity.getSupportFragmentManager()
68+
.findFragmentById(SettingsActivity.getFragmentHolderId());
69+
assertTrue(fragment instanceof MainSettingsFragment);
70+
}
71+
}

0 commit comments

Comments
 (0)