From 683d9816cb21124cc1f0f04ee26130cb5c4e5a4c Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 24 Dec 2021 21:27:45 +0100
Subject: [PATCH 01/27] Removed dead code
---
.../newpipe/settings/AppearanceSettingsFragment.java | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java
index 1e1b03b4f..4bc5a210e 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java
@@ -2,7 +2,6 @@ package org.schabi.newpipe.settings;
import android.content.ActivityNotFoundException;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.widget.Toast;
@@ -15,10 +14,6 @@ import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ThemeHelper;
public class AppearanceSettingsFragment extends BasePreferenceFragment {
- private static final boolean CAPTIONING_SETTINGS_ACCESSIBLE =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
-
- private String captionSettingsKey;
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
@@ -51,16 +46,11 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment {
} else {
removePreference(nightThemeKey);
}
-
- captionSettingsKey = getString(R.string.caption_settings_key);
- if (!CAPTIONING_SETTINGS_ACCESSIBLE) {
- removePreference(captionSettingsKey);
- }
}
@Override
public boolean onPreferenceTreeClick(final Preference preference) {
- if (preference.getKey().equals(captionSettingsKey) && CAPTIONING_SETTINGS_ACCESSIBLE) {
+ if (preference.getKey().equals(getString(R.string.caption_settings_key))) {
try {
startActivity(new Intent(Settings.ACTION_CAPTIONING_SETTINGS));
} catch (final ActivityNotFoundException e) {
From 12acaf29dd824ee21baadf26dceadd3ce0284aa7 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 24 Dec 2021 21:29:22 +0100
Subject: [PATCH 02/27] Added credit to the project which inspired the
preference search
---
app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
index a8fdcae26..1e5bd8799 100644
--- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
+++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
@@ -185,7 +185,11 @@ class AboutActivity : AppCompatActivity() {
SoftwareComponent(
"RxJava", "2016 - 2020", "RxJava Contributors",
"https://github.com/ReactiveX/RxJava", StandardLicenses.APACHE2
- )
+ ),
+ SoftwareComponent(
+ "SearchPreference", "2018", "ByteHamster",
+ "https://github.com/ByteHamster/SearchPreference", StandardLicenses.MIT
+ ),
)
private const val POS_ABOUT = 0
private const val POS_LICENSE = 1
From f3be89b503649067ba33638a3881c987adc3ec42 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 24 Dec 2021 21:30:49 +0100
Subject: [PATCH 03/27] Abstracted methods for the Android keyboard
---
.../fragments/list/search/SearchFragment.java | 23 ++--------
.../org/schabi/newpipe/util/KeyboardUtil.java | 43 +++++++++++++++++++
2 files changed, 46 insertions(+), 20 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/util/KeyboardUtil.java
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
index 15424334d..055c27733 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
@@ -25,7 +25,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
@@ -34,7 +33,6 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.TooltipCompat;
-import androidx.core.content.ContextCompat;
import androidx.core.text.HtmlCompat;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.ItemTouchHelper;
@@ -65,6 +63,7 @@ import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ExtractorHelper;
+import org.schabi.newpipe.util.KeyboardUtil;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;
@@ -670,31 +669,15 @@ public class SearchFragment extends BaseListFragment
+ * See also https://stackoverflow.com/q/1109022
+ *
+ */
+public final class KeyboardUtil {
+ private KeyboardUtil() {
+ }
+
+ public static void showKeyboard(final Activity activity, final EditText editText) {
+ if (activity == null || editText == null) {
+ return;
+ }
+
+ if (editText.requestFocus()) {
+ final InputMethodManager imm = ContextCompat.getSystemService(activity,
+ InputMethodManager.class);
+ imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
+ }
+ }
+
+ public static void hideKeyboard(final Activity activity, final EditText editText) {
+ if (activity == null || editText == null) {
+ return;
+ }
+
+ final InputMethodManager imm = ContextCompat.getSystemService(activity,
+ InputMethodManager.class);
+ imm.hideSoftInputFromWindow(editText.getWindowToken(),
+ InputMethodManager.RESULT_UNCHANGED_SHOWN);
+
+ editText.clearFocus();
+ }
+}
From 4a061f20edc95467673600ca387393f2fa90ff30 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 24 Dec 2021 21:32:13 +0100
Subject: [PATCH 04/27] Code cleanup
---
.../newpipe/settings/SelectKioskFragment.java | 18 ------------------
.../settings/tabs/ChooseTabsFragment.java | 15 +++------------
app/src/main/res/layout/settings_layout.xml | 8 ++++----
app/src/main/res/xml/update_settings.xml | 1 -
4 files changed, 7 insertions(+), 35 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java
index a766ee074..383390506 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java
@@ -1,6 +1,5 @@
package org.schabi.newpipe.settings;
-import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -51,16 +50,11 @@ public class SelectKioskFragment extends DialogFragment {
private SelectKioskAdapter selectKioskAdapter = null;
private OnSelectedListener onSelectedListener = null;
- private OnCancelListener onCancelListener = null;
public void setOnSelectedListener(final OnSelectedListener listener) {
onSelectedListener = listener;
}
- public void setOnCancelListener(final OnCancelListener listener) {
- onCancelListener = listener;
- }
-
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@@ -91,14 +85,6 @@ public class SelectKioskFragment extends DialogFragment {
// Handle actions
//////////////////////////////////////////////////////////////////////////*/
- @Override
- public void onCancel(@NonNull final DialogInterface dialogInterface) {
- super.onCancel(dialogInterface);
- if (onCancelListener != null) {
- onCancelListener.onCancel();
- }
- }
-
private void clickedItem(final SelectKioskAdapter.Entry entry) {
if (onSelectedListener != null) {
onSelectedListener.onKioskSelected(entry.serviceId, entry.kioskId, entry.kioskName);
@@ -114,10 +100,6 @@ public class SelectKioskFragment extends DialogFragment {
void onKioskSelected(int serviceId, String kioskId, String kioskName);
}
- public interface OnCancelListener {
- void onCancel();
- }
-
private class SelectKioskAdapter
extends RecyclerView.Adapter {
private final List kioskList = new Vector<>();
diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java
index 95f7f50ba..490e299bd 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java
@@ -44,8 +44,6 @@ import java.util.List;
import static org.schabi.newpipe.settings.tabs.Tab.typeFrom;
public class ChooseTabsFragment extends Fragment {
- private static final int MENU_ITEM_RESTORE_ID = 123456;
-
private TabsManager tabsManager;
private final List tabList = new ArrayList<>();
@@ -110,21 +108,14 @@ public class ChooseTabsFragment extends Fragment {
@NonNull final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- final MenuItem restoreItem = menu.add(Menu.NONE, MENU_ITEM_RESTORE_ID, Menu.NONE,
- R.string.restore_defaults);
+ final MenuItem restoreItem = menu.add(R.string.restore_defaults);
restoreItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
restoreItem.setIcon(AppCompatResources.getDrawable(requireContext(),
R.drawable.ic_settings_backup_restore));
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- if (item.getItemId() == MENU_ITEM_RESTORE_ID) {
+ restoreItem.setOnMenuItemClickListener(ev -> {
restoreDefaults();
return true;
- }
-
- return super.onOptionsItemSelected(item);
+ });
}
/*//////////////////////////////////////////////////////////////////////////
diff --git a/app/src/main/res/layout/settings_layout.xml b/app/src/main/res/layout/settings_layout.xml
index 33237d7b0..1b7b8b5e2 100644
--- a/app/src/main/res/layout/settings_layout.xml
+++ b/app/src/main/res/layout/settings_layout.xml
@@ -6,14 +6,14 @@
android:orientation="vertical"
tools:context="org.schabi.newpipe.MainActivity">
+
+
-
-
diff --git a/app/src/main/res/xml/update_settings.xml b/app/src/main/res/xml/update_settings.xml
index ef121ec4e..a44555edf 100644
--- a/app/src/main/res/xml/update_settings.xml
+++ b/app/src/main/res/xml/update_settings.xml
@@ -1,7 +1,6 @@
Date: Fri, 24 Dec 2021 21:33:40 +0100
Subject: [PATCH 05/27] Added preference search "framework"
---
.../preferencesearch/PreferenceParser.java | 201 ++++++++++++++++++
.../PreferenceSearchAdapter.java | 91 ++++++++
.../PreferenceSearchConfiguration.java | 163 ++++++++++++++
.../PreferenceSearchFragment.java | 116 ++++++++++
.../PreferenceSearchItem.java | 91 ++++++++
.../PreferenceSearchResultHighlighter.java | 125 +++++++++++
.../PreferenceSearchResultListener.java | 7 +
.../preferencesearch/PreferenceSearcher.java | 36 ++++
.../preferencesearch/package-info.java | 10 +
9 files changed, 840 insertions(+)
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultListener.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/package-info.java
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
new file mode 100644
index 000000000..1cf401892
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
@@ -0,0 +1,201 @@
+package org.schabi.newpipe.settings.preferencesearch;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.PreferenceManager;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Parses the corresponding preference-file(s).
+ */
+class PreferenceParser {
+ private static final String TAG = "PreferenceParser";
+
+ private static final String NS_ANDROID = "http://schemas.android.com/apk/res/android";
+ private static final String NS_SEARCH = "http://schemas.android.com/apk/preferencesearch";
+
+ private final Context context;
+ private final Map allPreferences;
+ private final PreferenceSearchConfiguration searchConfiguration;
+
+ PreferenceParser(
+ final Context context,
+ final PreferenceSearchConfiguration searchConfiguration
+ ) {
+ this.context = context;
+ this.allPreferences = PreferenceManager.getDefaultSharedPreferences(context).getAll();
+ this.searchConfiguration = searchConfiguration;
+ }
+
+ public List parse(
+ final PreferenceSearchConfiguration.SearchIndexItem item
+ ) {
+ Objects.requireNonNull(item, "item can't be null");
+
+ final List results = new ArrayList<>();
+ final XmlPullParser xpp = context.getResources().getXml(item.getResId());
+
+ try {
+ xpp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ xpp.setFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES, true);
+
+ final List breadcrumbs = new ArrayList<>();
+ if (!TextUtils.isEmpty(item.getBreadcrumb())) {
+ breadcrumbs.add(item.getBreadcrumb());
+ }
+ while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if (xpp.getEventType() == XmlPullParser.START_TAG) {
+ final PreferenceSearchItem result = parseSearchResult(
+ xpp,
+ joinBreadcrumbs(breadcrumbs),
+ item.getResId()
+ );
+
+ if (!searchConfiguration.getParserIgnoreElements().contains(xpp.getName())
+ && result.hasData()
+ && !"true".equals(getAttribute(xpp, NS_SEARCH, "ignore"))) {
+ results.add(result);
+ }
+ if (searchConfiguration.getParserContainerElements().contains(xpp.getName())) {
+ breadcrumbs.add(result.getTitle() == null ? "" : result.getTitle());
+ }
+ } else if (xpp.getEventType() == XmlPullParser.END_TAG
+ && searchConfiguration.getParserContainerElements()
+ .contains(xpp.getName())) {
+ breadcrumbs.remove(breadcrumbs.size() - 1);
+ }
+
+ xpp.next();
+ }
+ } catch (final Exception e) {
+ Log.w(TAG, "Failed to parse resid=" + item.getResId(), e);
+ }
+ return results;
+ }
+
+ private String joinBreadcrumbs(final List breadcrumbs) {
+ return breadcrumbs.stream()
+ .filter(crumb -> !TextUtils.isEmpty(crumb))
+ .reduce("", searchConfiguration.getBreadcrumbConcat());
+ }
+
+ private String getAttribute(
+ final XmlPullParser xpp,
+ @NonNull final String attribute
+ ) {
+ final String nsSearchAttr = getAttribute(xpp, NS_SEARCH, attribute);
+ if (nsSearchAttr != null) {
+ return nsSearchAttr;
+ }
+ return getAttribute(xpp, NS_ANDROID, attribute);
+ }
+
+ private String getAttribute(
+ final XmlPullParser xpp,
+ @NonNull final String namespace,
+ @NonNull final String attribute
+ ) {
+ return xpp.getAttributeValue(namespace, attribute);
+ }
+
+ private PreferenceSearchItem parseSearchResult(
+ final XmlPullParser xpp,
+ final String breadcrumbs,
+ final int searchIndexItemResId
+ ) {
+ final String key = readString(getAttribute(xpp, "key"));
+ final String[] entries = readStringArray(getAttribute(xpp, "entries"));
+ final String[] entryValues = readStringArray(getAttribute(xpp, "entryValues"));
+
+ return new PreferenceSearchItem(
+ key,
+ tryFillInPreferenceValue(
+ readString(getAttribute(xpp, "title")),
+ key,
+ entries,
+ entryValues),
+ tryFillInPreferenceValue(
+ readString(getAttribute(xpp, "summary")),
+ key,
+ entries,
+ entryValues),
+ TextUtils.join(",", entries),
+ readString(getAttribute(xpp, NS_SEARCH, "keywords")),
+ breadcrumbs,
+ searchIndexItemResId
+ );
+ }
+
+ private String[] readStringArray(@Nullable final String s) {
+ if (s == null) {
+ return new String[0];
+ }
+ if (s.startsWith("@")) {
+ try {
+ return context.getResources().getStringArray(Integer.parseInt(s.substring(1)));
+ } catch (final Exception e) {
+ Log.w(TAG, "Unable to readStringArray from '" + s + "'", e);
+ }
+ }
+ return new String[0];
+ }
+
+ private String readString(@Nullable final String s) {
+ if (s == null) {
+ return "";
+ }
+ if (s.startsWith("@")) {
+ try {
+ return context.getString(Integer.parseInt(s.substring(1)));
+ } catch (final Exception e) {
+ Log.w(TAG, "Unable to readString from '" + s + "'", e);
+ }
+ }
+ return s;
+ }
+
+ private String tryFillInPreferenceValue(
+ @Nullable final String s,
+ @Nullable final String key,
+ final String[] entries,
+ final String[] entryValues
+ ) {
+ if (s == null) {
+ return "";
+ }
+ if (key == null) {
+ return s;
+ }
+
+ // Resolve value
+ Object prefValue = allPreferences.get(key);
+ if (prefValue == null) {
+ return s;
+ }
+
+ /*
+ * Resolve ListPreference values
+ *
+ * entryValues = Values/Keys that are saved
+ * entries = Actual human readable names
+ */
+ if (entries.length > 0 && entryValues.length == entries.length) {
+ final int entryIndex = Arrays.asList(entryValues).indexOf(prefValue);
+ if (entryIndex != -1) {
+ prefValue = entries[entryIndex];
+ }
+ }
+
+ return String.format(s, prefValue.toString());
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java
new file mode 100644
index 000000000..527a4a595
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java
@@ -0,0 +1,91 @@
+package org.schabi.newpipe.settings.preferencesearch;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.schabi.newpipe.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+class PreferenceSearchAdapter
+ extends RecyclerView.Adapter {
+ private List dataset = new ArrayList<>();
+ private Consumer onItemClickListener;
+
+ @NonNull
+ @Override
+ public PreferenceSearchAdapter.PreferenceViewHolder onCreateViewHolder(
+ @NonNull final ViewGroup parent,
+ final int viewType
+ ) {
+ return new PreferenceViewHolder(
+ LayoutInflater
+ .from(parent.getContext())
+ .inflate(R.layout.settings_preferencesearch_list_item_result, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(
+ @NonNull final PreferenceSearchAdapter.PreferenceViewHolder holder,
+ final int position
+ ) {
+ final PreferenceSearchItem item = dataset.get(position);
+
+ holder.title.setText(item.getTitle());
+
+ if (TextUtils.isEmpty(item.getSummary())) {
+ holder.summary.setVisibility(View.GONE);
+ } else {
+ holder.summary.setVisibility(View.VISIBLE);
+ holder.summary.setText(item.getSummary());
+ }
+
+ if (TextUtils.isEmpty(item.getBreadcrumbs())) {
+ holder.breadcrumbs.setVisibility(View.GONE);
+ } else {
+ holder.breadcrumbs.setVisibility(View.VISIBLE);
+ holder.breadcrumbs.setText(item.getBreadcrumbs());
+ }
+
+ holder.itemView.setOnClickListener(v -> {
+ if (onItemClickListener != null) {
+ onItemClickListener.accept(item);
+ }
+ });
+ }
+
+ void setContent(final List items) {
+ dataset = new ArrayList<>(items);
+ this.notifyDataSetChanged();
+ }
+
+ @Override
+ public int getItemCount() {
+ return dataset.size();
+ }
+
+ void setOnItemClickListener(final Consumer onItemClickListener) {
+ this.onItemClickListener = onItemClickListener;
+ }
+
+ static class PreferenceViewHolder extends RecyclerView.ViewHolder {
+ final TextView title;
+ final TextView summary;
+ final TextView breadcrumbs;
+
+ PreferenceViewHolder(final View v) {
+ super(v);
+ title = v.findViewById(R.id.title);
+ summary = v.findViewById(R.id.summary);
+ breadcrumbs = v.findViewById(R.id.breadcrumbs);
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
new file mode 100644
index 000000000..b4d1c8985
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
@@ -0,0 +1,163 @@
+package org.schabi.newpipe.settings.preferencesearch;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.XmlRes;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BinaryOperator;
+import java.util.stream.Stream;
+
+public class PreferenceSearchConfiguration {
+ private final ArrayList itemsToIndex = new ArrayList<>();
+
+ private BinaryOperator breadcrumbConcat =
+ (s1, s2) -> TextUtils.isEmpty(s1) ? s2 : (s1 + " > " + s2);
+
+ private PreferenceSearchFunction searcher =
+ (itemStream, keyword) ->
+ itemStream
+ // Filter the items by the keyword
+ .filter(item -> item.getAllRelevantSearchFields().stream()
+ .filter(str -> !TextUtils.isEmpty(str))
+ .anyMatch(str ->
+ str.toLowerCase().contains(keyword.toLowerCase())))
+ // Limit the search results
+ .limit(100);
+
+ private final List parserIgnoreElements = Arrays.asList(
+ PreferenceCategory.class.getSimpleName());
+ private final List parserContainerElements = Arrays.asList(
+ PreferenceCategory.class.getSimpleName(),
+ PreferenceScreen.class.getSimpleName());
+
+
+ public void setBreadcrumbConcat(final BinaryOperator breadcrumbConcat) {
+ this.breadcrumbConcat = Objects.requireNonNull(breadcrumbConcat);
+ }
+
+ public void setSearcher(final PreferenceSearchFunction searcher) {
+ this.searcher = Objects.requireNonNull(searcher);
+ }
+
+ /**
+ * Adds a new file to the index.
+ *
+ * @param resId The preference file to index
+ * @return SearchIndexItem
+ */
+ public SearchIndexItem index(@XmlRes final int resId) {
+ final SearchIndexItem item = new SearchIndexItem(resId, this);
+ itemsToIndex.add(item);
+ return item;
+ }
+
+ List getFiles() {
+ return itemsToIndex;
+ }
+
+ public BinaryOperator getBreadcrumbConcat() {
+ return breadcrumbConcat;
+ }
+
+ public PreferenceSearchFunction getSearchMatcher() {
+ return searcher;
+ }
+
+ public List getParserIgnoreElements() {
+ return parserIgnoreElements;
+ }
+
+ public List getParserContainerElements() {
+ return parserContainerElements;
+ }
+
+ /**
+ * Adds a given R.xml resource to the search index.
+ */
+ public static final class SearchIndexItem implements Parcelable {
+ private String breadcrumb = "";
+ @XmlRes
+ private final int resId;
+ private final PreferenceSearchConfiguration searchConfiguration;
+
+ /**
+ * Includes the given R.xml resource in the index.
+ *
+ * @param resId The resource to index
+ * @param searchConfiguration The configuration for the search
+ */
+ private SearchIndexItem(
+ @XmlRes final int resId,
+ final PreferenceSearchConfiguration searchConfiguration
+ ) {
+ this.resId = resId;
+ this.searchConfiguration = searchConfiguration;
+ }
+
+ /**
+ * Adds a breadcrumb.
+ *
+ * @param breadcrumb The breadcrumb to add
+ * @return For chaining
+ */
+ @SuppressWarnings("HiddenField")
+ public SearchIndexItem withBreadcrumb(final String breadcrumb) {
+ this.breadcrumb =
+ searchConfiguration.getBreadcrumbConcat().apply(this.breadcrumb, breadcrumb);
+ return this;
+ }
+
+ @XmlRes
+ int getResId() {
+ return resId;
+ }
+
+ String getBreadcrumb() {
+ return breadcrumb;
+ }
+
+ public static final Creator CREATOR = new Creator<>() {
+ @Override
+ public SearchIndexItem createFromParcel(final Parcel in) {
+ return new SearchIndexItem(in);
+ }
+
+ @Override
+ public SearchIndexItem[] newArray(final int size) {
+ return new SearchIndexItem[size];
+ }
+ };
+
+ private SearchIndexItem(final Parcel parcel) {
+ this.breadcrumb = parcel.readString();
+ this.resId = parcel.readInt();
+ this.searchConfiguration = null;
+ }
+
+ @Override
+ public void writeToParcel(final Parcel dest, final int flags) {
+ dest.writeString(this.breadcrumb);
+ dest.writeInt(this.resId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+ }
+
+ @FunctionalInterface
+ public interface PreferenceSearchFunction {
+ Stream search(
+ Stream allAvailable,
+ String keyword);
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
new file mode 100644
index 000000000..a90d1084e
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
@@ -0,0 +1,116 @@
+package org.schabi.newpipe.settings.preferencesearch;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.schabi.newpipe.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Displays the search results.
+ */
+public class PreferenceSearchFragment extends Fragment {
+ public static final String NAME = PreferenceSearchFragment.class.getSimpleName();
+
+ private final PreferenceSearchConfiguration searchConfiguration;
+
+ private final PreferenceSearcher searcher;
+ private SearchViewHolder viewHolder;
+ private PreferenceSearchAdapter adapter;
+
+ public PreferenceSearchFragment(final PreferenceSearchConfiguration searchConfiguration) {
+ this.searchConfiguration = searchConfiguration;
+ this.searcher = new PreferenceSearcher(searchConfiguration);
+ }
+
+ @Override
+ public void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final PreferenceParser parser =
+ new PreferenceParser(
+ getContext(),
+ searchConfiguration);
+
+ searchConfiguration.getFiles().stream()
+ .map(parser::parse)
+ .forEach(searcher::add);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull final LayoutInflater inflater,
+ @Nullable final ViewGroup container,
+ @Nullable final Bundle savedInstanceState
+ ) {
+ final View rootView =
+ inflater.inflate(R.layout.settings_preferencesearch_fragment, container, false);
+
+ viewHolder = new SearchViewHolder(rootView);
+ viewHolder.recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+
+ adapter = new PreferenceSearchAdapter();
+ adapter.setOnItemClickListener(this::onItemClicked);
+ viewHolder.recyclerView.setAdapter(adapter);
+
+ return rootView;
+ }
+
+ public void updateSearchResults(final String keyword) {
+ if (adapter == null) {
+ return;
+ }
+
+ final List results =
+ !TextUtils.isEmpty(keyword)
+ ? searcher.searchFor(keyword)
+ : new ArrayList<>();
+
+ adapter.setContent(new ArrayList<>(results));
+
+ setEmptyViewShown(!TextUtils.isEmpty(keyword) && results.isEmpty());
+ }
+
+ private void setEmptyViewShown(final boolean shown) {
+ viewHolder.emptyStateView.setVisibility(shown ? View.VISIBLE : View.GONE);
+ viewHolder.recyclerView.setVisibility(shown ? View.GONE : View.VISIBLE);
+ }
+
+ public void onItemClicked(final PreferenceSearchItem item) {
+ if (!(getActivity() instanceof PreferenceSearchResultListener)) {
+ throw new ClassCastException(
+ getActivity().toString() + " must implement SearchPreferenceResultListener");
+ }
+
+ ((PreferenceSearchResultListener) getActivity()).onSearchResultClicked(item);
+ }
+
+ @Override
+ public void onDestroy() {
+ searcher.close();
+ super.onDestroy();
+ }
+
+ private static class SearchViewHolder {
+ private final RecyclerView recyclerView;
+ private final View emptyStateView;
+
+ SearchViewHolder(final View root) {
+ recyclerView = Objects.requireNonNull(root.findViewById(R.id.list));
+ emptyStateView = Objects.requireNonNull(root.findViewById(R.id.empty_state_view));
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java
new file mode 100644
index 000000000..3030a78bb
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java
@@ -0,0 +1,91 @@
+package org.schabi.newpipe.settings.preferencesearch;
+
+import androidx.annotation.NonNull;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a preference-item inside the search.
+ */
+public class PreferenceSearchItem {
+ @NonNull
+ private final String key;
+ @NonNull
+ private final String title;
+ @NonNull
+ private final String summary;
+ @NonNull
+ private final String entries;
+ @NonNull
+ private final String keywords;
+ @NonNull
+ private final String breadcrumbs;
+ private final int searchIndexItemResId;
+
+ public PreferenceSearchItem(
+ @NonNull final String key,
+ @NonNull final String title,
+ @NonNull final String summary,
+ @NonNull final String entries,
+ @NonNull final String keywords,
+ @NonNull final String breadcrumbs,
+ final int searchIndexItemResId
+ ) {
+ this.key = Objects.requireNonNull(key);
+ this.title = Objects.requireNonNull(title);
+ this.summary = Objects.requireNonNull(summary);
+ this.entries = Objects.requireNonNull(entries);
+ this.keywords = Objects.requireNonNull(keywords);
+ this.breadcrumbs = Objects.requireNonNull(breadcrumbs);
+ this.searchIndexItemResId = searchIndexItemResId;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getSummary() {
+ return summary;
+ }
+
+ public String getEntries() {
+ return entries;
+ }
+
+ public String getBreadcrumbs() {
+ return breadcrumbs;
+ }
+
+ public String getKeywords() {
+ return keywords;
+ }
+
+ public int getSearchIndexItemResId() {
+ return searchIndexItemResId;
+ }
+
+ boolean hasData() {
+ return !key.isEmpty() && !title.isEmpty();
+ }
+
+ public List getAllRelevantSearchFields() {
+ return Arrays.asList(
+ getTitle(),
+ getSummary(),
+ getEntries(),
+ getBreadcrumbs(),
+ getKeywords());
+ }
+
+
+ @Override
+ public String toString() {
+ return "PreferenceItem: " + title + " " + summary + " " + key;
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java
new file mode 100644
index 000000000..4ddb2caa8
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java
@@ -0,0 +1,125 @@
+package org.schabi.newpipe.settings.preferencesearch;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.util.TypedValue;
+
+import androidx.appcompat.content.res.AppCompatResources;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.schabi.newpipe.R;
+
+
+public final class PreferenceSearchResultHighlighter {
+ private static final String TAG = "PrefSearchResHighlter";
+
+ private PreferenceSearchResultHighlighter() {
+ }
+
+ /**
+ * Highlight the specified preference.
+ *
+ * @param item
+ * @param prefsFragment
+ */
+ public static void highlight(
+ final PreferenceSearchItem item,
+ final PreferenceFragmentCompat prefsFragment
+ ) {
+ new Handler(Looper.getMainLooper()).post(() -> doHighlight(item, prefsFragment));
+ }
+
+ private static void doHighlight(
+ final PreferenceSearchItem item,
+ final PreferenceFragmentCompat prefsFragment
+ ) {
+ final Preference prefResult = prefsFragment.findPreference(item.getKey());
+
+ if (prefResult == null) {
+ Log.w(TAG, "Preference '" + item.getKey() + "' not found on '" + prefsFragment + "'");
+ return;
+ }
+
+ final RecyclerView recyclerView = prefsFragment.getListView();
+ final RecyclerView.Adapter> adapter = recyclerView.getAdapter();
+ if (adapter instanceof PreferenceGroup.PreferencePositionCallback) {
+ final int position = ((PreferenceGroup.PreferencePositionCallback) adapter)
+ .getPreferenceAdapterPosition(prefResult);
+ if (position != RecyclerView.NO_POSITION) {
+ recyclerView.scrollToPosition(position);
+ recyclerView.postDelayed(() -> {
+ final RecyclerView.ViewHolder holder =
+ recyclerView.findViewHolderForAdapterPosition(position);
+ if (holder != null) {
+ final Drawable background = holder.itemView.getBackground();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
+ && background instanceof RippleDrawable) {
+ showRippleAnimation((RippleDrawable) background);
+ return;
+ }
+ }
+ highlightFallback(prefsFragment, prefResult);
+ }, 150);
+ return;
+ }
+ }
+ highlightFallback(prefsFragment, prefResult);
+ }
+
+ /**
+ * Alternative highlighting (shows an → arrow in front of the setting)if ripple does not work.
+ *
+ * @param prefsFragment
+ * @param prefResult
+ */
+ private static void highlightFallback(
+ final PreferenceFragmentCompat prefsFragment,
+ final Preference prefResult
+ ) {
+ // Get primary color from text for highlight icon
+ final TypedValue typedValue = new TypedValue();
+ final Resources.Theme theme = prefsFragment.getActivity().getTheme();
+ theme.resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
+ final TypedArray arr = prefsFragment.getActivity()
+ .obtainStyledAttributes(
+ typedValue.data,
+ new int[]{android.R.attr.textColorPrimary});
+ final int color = arr.getColor(0, 0xffE53935);
+ arr.recycle();
+
+ // Show highlight icon
+ final Drawable oldIcon = prefResult.getIcon();
+ final boolean oldSpaceReserved = prefResult.isIconSpaceReserved();
+ final Drawable highlightIcon =
+ AppCompatResources.getDrawable(
+ prefsFragment.requireContext(),
+ R.drawable.ic_play_arrow);
+ highlightIcon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
+ prefResult.setIcon(highlightIcon);
+
+ prefsFragment.scrollToPreference(prefResult);
+
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ prefResult.setIcon(oldIcon);
+ prefResult.setIconSpaceReserved(oldSpaceReserved);
+ }, 1000);
+ }
+
+ private static void showRippleAnimation(final RippleDrawable rippleDrawable) {
+ rippleDrawable.setState(
+ new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled});
+ new Handler(Looper.getMainLooper())
+ .postDelayed(() -> rippleDrawable.setState(new int[]{}), 1000);
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultListener.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultListener.java
new file mode 100644
index 000000000..1f0636454
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultListener.java
@@ -0,0 +1,7 @@
+package org.schabi.newpipe.settings.preferencesearch;
+
+import androidx.annotation.NonNull;
+
+public interface PreferenceSearchResultListener {
+ void onSearchResultClicked(@NonNull PreferenceSearchItem result);
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java
new file mode 100644
index 000000000..f9427a1ca
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java
@@ -0,0 +1,36 @@
+package org.schabi.newpipe.settings.preferencesearch;
+
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+class PreferenceSearcher implements AutoCloseable {
+ private final List allEntries = new ArrayList<>();
+
+ private final PreferenceSearchConfiguration configuration;
+
+ PreferenceSearcher(final PreferenceSearchConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ void add(final List items) {
+ allEntries.addAll(items);
+ }
+
+ List searchFor(final String keyword) {
+ if (TextUtils.isEmpty(keyword)) {
+ return new ArrayList<>();
+ }
+
+ return configuration.getSearchMatcher()
+ .search(allEntries.stream(), keyword)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public void close() {
+ allEntries.clear();
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/package-info.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/package-info.java
new file mode 100644
index 000000000..00929235e
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * Contains classes for searching inside the preferences.
+ *
+ * This code is based on
+ * ByteHamster/SearchPreference
+ * (MIT license) but was heavily modified/refactored for our use.
+ *
+ * @author litetex
+ */
+package org.schabi.newpipe.settings.preferencesearch;
From 07fb319e886f8bee02a4d1de96ecd2185999efa0 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 24 Dec 2021 21:35:15 +0100
Subject: [PATCH 06/27] Applied code changes for preference search framework
---
.../settings/AppearanceSettingsFragment.java | 2 +-
.../settings/BasePreferenceFragment.java | 5 +
.../settings/ContentSettingsFragment.java | 8 +-
.../settings/DownloadSettingsFragment.java | 2 +-
.../settings/HistorySettingsFragment.java | 2 +-
.../settings/MainSettingsFragment.java | 43 +++-
.../settings/NotificationSettingsFragment.kt | 2 +-
.../newpipe/settings/SettingsActivity.java | 206 +++++++++++++++++-
.../settings/SettingsResourceRegistry.java | 151 +++++++++++++
.../settings/UpdateSettingsFragment.java | 2 +-
.../settings/VideoAudioSettingsFragment.java | 2 +-
11 files changed, 408 insertions(+), 17 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java
diff --git a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java
index 4bc5a210e..e08562908 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java
@@ -17,7 +17,7 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment {
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
- addPreferencesFromResource(R.xml.appearance_settings);
+ addPreferencesFromResourceRegistry();
final String themeKey = getString(R.string.theme_key);
// the key of the active theme when settings were opened (or recreated after theme change)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java
index a745861ad..619579f3a 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java
@@ -28,6 +28,11 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
super.onCreate(savedInstanceState);
}
+ protected void addPreferencesFromResourceRegistry() {
+ addPreferencesFromResource(
+ SettingsResourceRegistry.getInstance().getPreferencesResId(this.getClass()));
+ }
+
@Override
public void onViewCreated(@NonNull final View rootView,
@Nullable final Bundle savedInstanceState) {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
index 1c8eb5cd2..d79ea0cd5 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
@@ -1,5 +1,8 @@
package org.schabi.newpipe.settings;
+import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
+import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -38,9 +41,6 @@ import java.util.Date;
import java.util.Locale;
import java.util.Objects;
-import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
-import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
-
public class ContentSettingsFragment extends BasePreferenceFragment {
private static final String ZIP_MIME_TYPE = "application/zip";
@@ -70,7 +70,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
importExportDataPathKey = getString(R.string.import_export_data_path);
youtubeRestrictedModeEnabledKey = getString(R.string.youtube_restricted_mode_enabled);
- addPreferencesFromResource(R.xml.content_settings);
+ addPreferencesFromResourceRegistry();
final Preference importDataPreference = requirePreference(R.string.import_data);
importDataPreference.setOnPreferenceClickListener((Preference p) -> {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
index 681aee409..fe327e1b5 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
@@ -54,7 +54,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
- addPreferencesFromResource(R.xml.download_settings);
+ addPreferencesFromResourceRegistry();
downloadPathVideoPreference = getString(R.string.download_path_video_key);
downloadPathAudioPreference = getString(R.string.download_path_audio_key);
diff --git a/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java
index 33e0ba16b..868618110 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java
@@ -29,7 +29,7 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
- addPreferencesFromResource(R.xml.history_settings);
+ addPreferencesFromResourceRegistry();
cacheWipeKey = getString(R.string.metadata_cache_wipe_key);
viewsHistoryClearKey = getString(R.string.clear_views_history_key);
diff --git a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java
index 12599b828..6cd165861 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java
@@ -1,7 +1,11 @@
package org.schabi.newpipe.settings;
import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import androidx.annotation.NonNull;
import androidx.preference.Preference;
import org.schabi.newpipe.App;
@@ -12,10 +16,15 @@ import org.schabi.newpipe.R;
public class MainSettingsFragment extends BasePreferenceFragment {
public static final boolean DEBUG = MainActivity.DEBUG;
+ private SettingsActivity settingsActivity;
+
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
- addPreferencesFromResource(R.xml.main_settings);
+ addPreferencesFromResourceRegistry();
+ setHasOptionsMenu(true); // Otherwise onCreateOptionsMenu is not called
+
+ // Check if the app is updatable
if (!CheckForNewAppVersion.isReleaseApk(App.getApp())) {
final Preference update
= findPreference(getString(R.string.update_pref_screen_key));
@@ -24,4 +33,36 @@ public class MainSettingsFragment extends BasePreferenceFragment {
defaultPreferences.edit().putBoolean(getString(R.string.update_app_key), false).apply();
}
}
+
+ @Override
+ public void onCreateOptionsMenu(
+ @NonNull final Menu menu,
+ @NonNull final MenuInflater inflater
+ ) {
+ super.onCreateOptionsMenu(menu, inflater);
+
+ // -- Link settings activity and register menu --
+ settingsActivity = (SettingsActivity) getActivity();
+
+ inflater.inflate(R.menu.menu_settings_main_fragment, menu);
+
+ final MenuItem menuSearchItem = menu.getItem(0);
+
+ settingsActivity.setMenuSearchItem(menuSearchItem);
+
+ menuSearchItem.setOnMenuItemClickListener(ev -> {
+ settingsActivity.setSearchActive(true);
+ return true;
+ });
+ }
+
+ @Override
+ public void onDestroy() {
+ // Unlink activity so that we don't get memory problems
+ if (settingsActivity != null) {
+ settingsActivity.setMenuSearchItem(null);
+ settingsActivity = null;
+ }
+ super.onDestroy();
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt
index e03aa4074..6bea8b69e 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt
@@ -7,7 +7,7 @@ import org.schabi.newpipe.R
class NotificationSettingsFragment : BasePreferenceFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- addPreferencesFromResource(R.xml.notification_settings)
+ addPreferencesFromResourceRegistry()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
val colorizePref: Preference? = findPreference(getString(R.string.notification_colorize_key))
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 02e2538c5..787740cc2 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -1,22 +1,42 @@
package org.schabi.newpipe.settings;
+import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
+
import android.os.Bundle;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
+import android.widget.EditText;
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
+import com.jakewharton.rxbinding4.widget.RxTextView;
+
+import org.schabi.newpipe.App;
+import org.schabi.newpipe.CheckForNewAppVersion;
+import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.SettingsLayoutBinding;
+import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchConfiguration;
+import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchFragment;
+import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchItem;
+import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchResultHighlighter;
+import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchResultListener;
import org.schabi.newpipe.util.DeviceUtils;
+import org.schabi.newpipe.util.KeyboardUtil;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.FocusOverlayView;
-import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
+import java.util.concurrent.TimeUnit;
/*
* Created by Christian Schabesberger on 31.08.15.
@@ -39,7 +59,23 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
*/
public class SettingsActivity extends AppCompatActivity
- implements BasePreferenceFragment.OnPreferenceStartFragmentCallback {
+ implements
+ BasePreferenceFragment.OnPreferenceStartFragmentCallback,
+ PreferenceSearchResultListener {
+ private static final String TAG = "SettingsActivity";
+ private static final boolean DEBUG = MainActivity.DEBUG;
+
+ @IdRes
+ private static final int FRAGMENT_HOLDER_ID = R.id.settings_fragment_holder;
+
+ private PreferenceSearchFragment searchFragment;
+
+ @Nullable
+ private MenuItem menuSearchItem;
+
+ private View searchContainer;
+ private EditText searchEditText;
+
@Override
protected void onCreate(final Bundle savedInstanceBundle) {
setTheme(ThemeHelper.getSettingsThemeStyle(this));
@@ -49,6 +85,7 @@ public class SettingsActivity extends AppCompatActivity
final SettingsLayoutBinding settingsLayoutBinding =
SettingsLayoutBinding.inflate(getLayoutInflater());
setContentView(settingsLayoutBinding.getRoot());
+ initSearch(settingsLayoutBinding);
setSupportActionBar(settingsLayoutBinding.settingsToolbarLayout.toolbar);
@@ -78,6 +115,12 @@ public class SettingsActivity extends AppCompatActivity
public boolean onOptionsItemSelected(final MenuItem item) {
final int id = item.getItemId();
if (id == android.R.id.home) {
+ // Check if the search is active and if so: Close it
+ if (isSearchActive()) {
+ setSearchActive(false);
+ return true;
+ }
+
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish();
} else {
@@ -91,14 +134,165 @@ public class SettingsActivity extends AppCompatActivity
@Override
public boolean onPreferenceStartFragment(final PreferenceFragmentCompat caller,
final Preference preference) {
- final Fragment fragment = Fragment
- .instantiate(this, preference.getFragment(), preference.getExtras());
+ showSettingsFragment(instantiateFragment(preference.getFragment()));
+ return true;
+ }
+
+ private Fragment instantiateFragment(@NonNull final String className) {
+ return getSupportFragmentManager()
+ .getFragmentFactory()
+ .instantiate(this.getClassLoader(), className);
+ }
+
+ private void showSettingsFragment(final Fragment fragment) {
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.animator.custom_fade_in, R.animator.custom_fade_out,
R.animator.custom_fade_in, R.animator.custom_fade_out)
- .replace(R.id.settings_fragment_holder, fragment)
+ .replace(FRAGMENT_HOLDER_ID, fragment)
.addToBackStack(null)
.commit();
- return true;
}
+
+ @Override
+ protected void onDestroy() {
+ setMenuSearchItem(null);
+ super.onDestroy();
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Search
+ //////////////////////////////////////////////////////////////////////////*/
+ //region Search
+
+ private void initSearch(final SettingsLayoutBinding settingsLayoutBinding) {
+ searchContainer =
+ settingsLayoutBinding.settingsToolbarLayout.toolbar
+ .findViewById(R.id.toolbar_search_container);
+
+ // Configure input field for search
+ searchEditText = searchContainer.findViewById(R.id.toolbar_search_edit_text);
+ RxTextView.textChanges(searchEditText)
+ // Wait some time after the last input before actually searching
+ .debounce(200, TimeUnit.MILLISECONDS)
+ .subscribe(v -> runOnUiThread(() -> onSearchChanged()));
+
+ // Configure clear button
+ searchContainer.findViewById(R.id.toolbar_search_clear)
+ .setOnClickListener(ev -> resetSearchText());
+
+ // Build search configuration using SettingsResourceRegistry
+ prepareSearchConfig();
+
+ final PreferenceSearchConfiguration config = new PreferenceSearchConfiguration();
+ SettingsResourceRegistry.getInstance().getAllEntries().stream()
+ .filter(SettingsResourceRegistry.SettingRegistryEntry::isSearchable)
+ .map(SettingsResourceRegistry.SettingRegistryEntry::getPreferencesResId)
+ .forEach(config::index);
+
+ searchFragment = new PreferenceSearchFragment(config);
+ }
+
+ private void prepareSearchConfig() {
+ // Check if the update settings should be available
+ if (!CheckForNewAppVersion.isReleaseApk(App.getApp())) {
+ SettingsResourceRegistry.getInstance()
+ .getEntryByPreferencesResId(R.xml.update_settings)
+ .setSearchable(false);
+ }
+ }
+
+ public void setMenuSearchItem(final MenuItem menuSearchItem) {
+ this.menuSearchItem = menuSearchItem;
+ }
+
+ public void setSearchActive(final boolean active) {
+ // Ignore if search is already in correct state
+ if (isSearchActive() == active) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "setSearchActive called active=" + active);
+ }
+
+ searchContainer.setVisibility(active ? View.VISIBLE : View.GONE);
+ if (menuSearchItem != null) {
+ menuSearchItem.setVisible(!active);
+ }
+
+ final FragmentManager fm = getSupportFragmentManager();
+ if (active) {
+ fm.beginTransaction()
+ .add(FRAGMENT_HOLDER_ID, searchFragment, PreferenceSearchFragment.NAME)
+ .addToBackStack(PreferenceSearchFragment.NAME)
+ .commit();
+ } else if (searchFragment != null) {
+ fm.beginTransaction().remove(searchFragment).commit();
+ fm.popBackStack(
+ PreferenceSearchFragment.NAME,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+
+ KeyboardUtil.hideKeyboard(this, searchEditText);
+ }
+
+ resetSearchText();
+ }
+
+ private void resetSearchText() {
+ searchEditText.setText("");
+ }
+
+ private boolean isSearchActive() {
+ return searchContainer.getVisibility() == View.VISIBLE;
+ }
+
+ private void onSearchChanged() {
+ if (!isSearchActive()) {
+ return;
+ }
+
+ if (searchFragment != null) {
+ searchFragment.updateSearchResults(this.searchEditText.getText().toString());
+ }
+ }
+
+ @Override
+ public void onSearchResultClicked(@NonNull final PreferenceSearchItem result) {
+ if (DEBUG) {
+ Log.d(TAG, "onSearchResultClicked called result=" + result);
+ }
+
+ // Hide the search
+ setSearchActive(false);
+
+ // -- Highlight the result --
+ // Find out which fragment class we need
+ final Class extends Fragment> targetedFragmentClass =
+ SettingsResourceRegistry.getInstance()
+ .getFragmentClass(result.getSearchIndexItemResId());
+
+ if (targetedFragmentClass == null) {
+ // This should never happen
+ Log.w(TAG, "Unable to locate fragment class for resId="
+ + result.getSearchIndexItemResId());
+ return;
+ }
+
+ // Check if the currentFragment is the one which contains the result
+ Fragment currentFragment =
+ getSupportFragmentManager().findFragmentById(FRAGMENT_HOLDER_ID);
+ if (!targetedFragmentClass.equals(currentFragment.getClass())) {
+ // If it's not the correct one display the correct one
+ currentFragment = instantiateFragment(targetedFragmentClass.getName());
+ showSettingsFragment(currentFragment);
+ }
+
+ // Run the highlighting
+ if (currentFragment instanceof PreferenceFragmentCompat) {
+ PreferenceSearchResultHighlighter
+ .highlight(result, (PreferenceFragmentCompat) currentFragment);
+ }
+ }
+
+ //endregion
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java
new file mode 100644
index 000000000..c4db9f93d
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java
@@ -0,0 +1,151 @@
+package org.schabi.newpipe.settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.XmlRes;
+import androidx.fragment.app.Fragment;
+
+import org.schabi.newpipe.R;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A registry that contains information about SettingsFragments.
+ *
+ * includes:
+ *
+ * - Class of the SettingsFragment
+ * - XML-Resource
+ * - ...
+ *
+ *
+ * E.g. used by the preference search.
+ */
+public final class SettingsResourceRegistry {
+
+ private static final SettingsResourceRegistry INSTANCE = new SettingsResourceRegistry();
+
+ private final Set registeredEntries = new HashSet<>();
+
+ private SettingsResourceRegistry() {
+ add(MainSettingsFragment.class, R.xml.main_settings).setSearchable(false);
+
+ add(AppearanceSettingsFragment.class, R.xml.appearance_settings);
+ add(ContentSettingsFragment.class, R.xml.content_settings);
+ add(DownloadSettingsFragment.class, R.xml.download_settings);
+ add(HistorySettingsFragment.class, R.xml.history_settings);
+ add(NotificationSettingsFragment.class, R.xml.notification_settings);
+ add(UpdateSettingsFragment.class, R.xml.update_settings);
+ add(VideoAudioSettingsFragment.class, R.xml.video_audio_settings);
+ }
+
+ private SettingRegistryEntry add(
+ @NonNull final Class extends Fragment> fragmentClass,
+ @XmlRes final int preferencesResId
+ ) {
+ final SettingRegistryEntry entry =
+ new SettingRegistryEntry(fragmentClass, preferencesResId);
+ this.registeredEntries.add(entry);
+ return entry;
+ }
+
+ @Nullable
+ public SettingRegistryEntry getEntryByFragmentClass(
+ final Class extends Fragment> fragmentClass
+ ) {
+ Objects.requireNonNull(fragmentClass);
+ return registeredEntries.stream()
+ .filter(e -> Objects.equals(e.getFragmentClass(), fragmentClass))
+ .findFirst()
+ .orElse(null);
+ }
+
+ @Nullable
+ public SettingRegistryEntry getEntryByPreferencesResId(@XmlRes final int preferencesResId) {
+ return registeredEntries.stream()
+ .filter(e -> Objects.equals(e.getPreferencesResId(), preferencesResId))
+ .findFirst()
+ .orElse(null);
+ }
+
+ public int getPreferencesResId(@NonNull final Class extends Fragment> fragmentClass) {
+ final SettingRegistryEntry entry = getEntryByFragmentClass(fragmentClass);
+ if (entry == null) {
+ return -1;
+ }
+ return entry.getPreferencesResId();
+ }
+
+ @Nullable
+ public Class extends Fragment> getFragmentClass(@XmlRes final int preferencesResId) {
+ final SettingRegistryEntry entry = getEntryByPreferencesResId(preferencesResId);
+ if (entry == null) {
+ return null;
+ }
+ return entry.getFragmentClass();
+ }
+
+ public Set getAllEntries() {
+ return new HashSet<>(registeredEntries);
+ }
+
+ public static SettingsResourceRegistry getInstance() {
+ return INSTANCE;
+ }
+
+
+ public static class SettingRegistryEntry {
+ @NonNull
+ private final Class extends Fragment> fragmentClass;
+ @XmlRes
+ private final int preferencesResId;
+
+ private boolean searchable = true;
+
+ public SettingRegistryEntry(
+ @NonNull final Class extends Fragment> fragmentClass,
+ @XmlRes final int preferencesResId
+ ) {
+ this.fragmentClass = Objects.requireNonNull(fragmentClass);
+ this.preferencesResId = preferencesResId;
+ }
+
+ @SuppressWarnings("HiddenField")
+ public SettingRegistryEntry setSearchable(final boolean searchable) {
+ this.searchable = searchable;
+ return this;
+ }
+
+ public Class extends Fragment> getFragmentClass() {
+ return fragmentClass;
+ }
+
+ public int getPreferencesResId() {
+ return preferencesResId;
+ }
+
+ public boolean isSearchable() {
+ return searchable;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final SettingRegistryEntry that = (SettingRegistryEntry) o;
+ return getPreferencesResId() == that.getPreferencesResId()
+ && getFragmentClass().equals(that.getFragmentClass());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getFragmentClass(), getPreferencesResId());
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java
index bc183d08a..04bad3815 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java
@@ -38,7 +38,7 @@ public class UpdateSettingsFragment extends BasePreferenceFragment {
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
- addPreferencesFromResource(R.xml.update_settings);
+ addPreferencesFromResourceRegistry();
findPreference(getString(R.string.update_app_key))
.setOnPreferenceChangeListener(updatePreferenceChange);
diff --git a/app/src/main/java/org/schabi/newpipe/settings/VideoAudioSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/VideoAudioSettingsFragment.java
index c0d274fe0..039f00c1d 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/VideoAudioSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/VideoAudioSettingsFragment.java
@@ -23,7 +23,7 @@ public class VideoAudioSettingsFragment extends BasePreferenceFragment {
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
- addPreferencesFromResource(R.xml.video_audio_settings);
+ addPreferencesFromResourceRegistry();
updateSeekOptions();
From 8fc935b09db56f597b824e248f445cd7b30579d4 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 24 Dec 2021 21:35:41 +0100
Subject: [PATCH 07/27] Added resource files
Forgot to commit them before...
---
.../settings_preferencesearch_fragment.xml | 49 +++++++++++++++++++
...ings_preferencesearch_list_item_result.xml | 36 ++++++++++++++
.../res/menu/menu_settings_main_fragment.xml | 11 +++++
3 files changed, 96 insertions(+)
create mode 100644 app/src/main/res/layout/settings_preferencesearch_fragment.xml
create mode 100644 app/src/main/res/layout/settings_preferencesearch_list_item_result.xml
create mode 100644 app/src/main/res/menu/menu_settings_main_fragment.xml
diff --git a/app/src/main/res/layout/settings_preferencesearch_fragment.xml b/app/src/main/res/layout/settings_preferencesearch_fragment.xml
new file mode 100644
index 000000000..b8aaa60f6
--- /dev/null
+++ b/app/src/main/res/layout/settings_preferencesearch_fragment.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/settings_preferencesearch_list_item_result.xml b/app/src/main/res/layout/settings_preferencesearch_list_item_result.xml
new file mode 100644
index 000000000..2e20f274c
--- /dev/null
+++ b/app/src/main/res/layout/settings_preferencesearch_list_item_result.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_settings_main_fragment.xml b/app/src/main/res/menu/menu_settings_main_fragment.xml
new file mode 100644
index 000000000..fbe3b4e09
--- /dev/null
+++ b/app/src/main/res/menu/menu_settings_main_fragment.xml
@@ -0,0 +1,11 @@
+
+
From 22db4175f37205f650df7a67f3017705404eda2b Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 24 Dec 2021 21:37:04 +0100
Subject: [PATCH 08/27] Moved reset-reCAPTCHA-cookie to cache tab and made it
read-only
so that the search works as expected
---
.../settings/ContentSettingsFragment.java | 16 ----------------
.../settings/HistorySettingsFragment.java | 17 +++++++++++++++++
app/src/main/res/xml/content_settings.xml | 7 -------
app/src/main/res/xml/history_settings.xml | 7 +++++++
4 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
index d79ea0cd5..47458ad3f 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
@@ -24,7 +24,6 @@ import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorUtil;
-import org.schabi.newpipe.error.ReCaptchaActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
@@ -105,21 +104,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
.getPreferredContentCountry(requireContext());
initialLanguage = defaultPreferences.getString(getString(R.string.app_language_key), "en");
- final Preference clearCookiePref = requirePreference(R.string.clear_cookie_key);
- clearCookiePref.setOnPreferenceClickListener(preference -> {
- defaultPreferences.edit()
- .putString(getString(R.string.recaptcha_cookies_key), "").apply();
- DownloaderImpl.getInstance().setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, "");
- Toast.makeText(getActivity(), R.string.recaptcha_cookies_cleared,
- Toast.LENGTH_SHORT).show();
- clearCookiePref.setVisible(false);
- return true;
- });
-
- if (defaultPreferences.getString(getString(R.string.recaptcha_cookies_key), "").isEmpty()) {
- clearCookiePref.setVisible(false);
- }
-
findPreference(getString(R.string.download_thumbnail_key)).setOnPreferenceChangeListener(
(preference, newValue) -> {
PicassoHelper.setShouldLoadImages((Boolean) newValue);
diff --git a/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java
index 868618110..86e651e2b 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java
@@ -8,9 +8,11 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
+import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.ErrorUtil;
+import org.schabi.newpipe.error.ReCaptchaActivity;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.InfoCache;
@@ -37,6 +39,21 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
searchHistoryClearKey = getString(R.string.clear_search_history_key);
recordManager = new HistoryRecordManager(getActivity());
disposables = new CompositeDisposable();
+
+ final Preference clearCookiePref = requirePreference(R.string.clear_cookie_key);
+ clearCookiePref.setOnPreferenceClickListener(preference -> {
+ defaultPreferences.edit()
+ .putString(getString(R.string.recaptcha_cookies_key), "").apply();
+ DownloaderImpl.getInstance().setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, "");
+ Toast.makeText(getActivity(), R.string.recaptcha_cookies_cleared,
+ Toast.LENGTH_SHORT).show();
+ clearCookiePref.setEnabled(false);
+ return true;
+ });
+
+ if (defaultPreferences.getString(getString(R.string.recaptcha_cookies_key), "").isEmpty()) {
+ clearCookiePref.setEnabled(false);
+ }
}
@Override
diff --git a/app/src/main/res/xml/content_settings.xml b/app/src/main/res/xml/content_settings.xml
index 23b782ffd..e754b3a30 100644
--- a/app/src/main/res/xml/content_settings.xml
+++ b/app/src/main/res/xml/content_settings.xml
@@ -128,13 +128,6 @@
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
-
-
+
+
From 7fc0a3841a206e0b82f887e7da82cb47eff18734 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 24 Dec 2021 21:51:44 +0100
Subject: [PATCH 09/27] Fine tuning
---
.../main/java/org/schabi/newpipe/settings/SettingsActivity.java | 2 ++
.../preferencesearch/PreferenceSearchResultHighlighter.java | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 787740cc2..88c8fcba5 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -226,6 +226,8 @@ public class SettingsActivity extends AppCompatActivity
.add(FRAGMENT_HOLDER_ID, searchFragment, PreferenceSearchFragment.NAME)
.addToBackStack(PreferenceSearchFragment.NAME)
.commit();
+
+ KeyboardUtil.showKeyboard(this, searchEditText);
} else if (searchFragment != null) {
fm.beginTransaction().remove(searchFragment).commit();
fm.popBackStack(
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java
index 4ddb2caa8..e48ca28ce 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java
@@ -70,7 +70,7 @@ public final class PreferenceSearchResultHighlighter {
}
}
highlightFallback(prefsFragment, prefResult);
- }, 150);
+ }, 200);
return;
}
}
From 52542e04e82e36a4c201b29c98b6bef11e81fe2d Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sat, 25 Dec 2021 00:06:06 +0100
Subject: [PATCH 10/27] Added fuzzy searching + Some minor code refactoring
---
.../newpipe/settings/SettingsActivity.java | 9 ++
.../PreferenceFuzzySearchFunction.java | 121 ++++++++++++++
.../PreferenceSearchConfiguration.java | 11 +-
.../PreferenceSearchFragment.java | 2 +-
.../similarity/FuzzyScore.java | 148 ++++++++++++++++++
5 files changed, 280 insertions(+), 11 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/preferencesearch/similarity/FuzzyScore.java
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 88c8fcba5..1f917e771 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -111,6 +111,15 @@ public class SettingsActivity extends AppCompatActivity
return super.onCreateOptionsMenu(menu);
}
+ @Override
+ public void onBackPressed() {
+ if (isSearchActive()) {
+ setSearchActive(false);
+ return;
+ }
+ super.onBackPressed();
+ }
+
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
final int id = item.getItemId();
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java
new file mode 100644
index 000000000..48f507a41
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java
@@ -0,0 +1,121 @@
+package org.schabi.newpipe.settings.preferencesearch;
+
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import org.schabi.newpipe.settings.preferencesearch.similarity.FuzzyScore;
+
+import java.util.Comparator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+public class PreferenceFuzzySearchFunction
+ implements PreferenceSearchConfiguration.PreferenceSearchFunction {
+
+ private static final FuzzyScore FUZZY_SCORE = new FuzzyScore(Locale.ROOT);
+
+ @Override
+ public Stream search(
+ final Stream allAvailable,
+ final String keyword
+ ) {
+ final float maxScore = (keyword.length() + 1) * 3 - 2; // First can't get +2 bonus score
+
+ return allAvailable
+ // General search
+ // Check all fields if anyone contains something that kind of matches the keyword
+ .map(item -> new FuzzySearchGeneralDTO(item, keyword))
+ .filter(dto -> dto.getScore() / maxScore >= 0.3f)
+ .map(FuzzySearchGeneralDTO::getItem)
+ // Specific search - Used for determining order of search results
+ // Calculate a score based on specific search fields
+ .map(item -> new FuzzySearchSpecificDTO(item, keyword))
+ .sorted(Comparator.comparing(FuzzySearchSpecificDTO::getScore).reversed())
+ .map(FuzzySearchSpecificDTO::getItem)
+ // Limit the amount of search results
+ .limit(20);
+ }
+
+ private float computeFuzzyScore(
+ @NonNull final PreferenceSearchItem item,
+ @NonNull final Function resolver,
+ @NonNull final String keyword
+ ) {
+ return FUZZY_SCORE.fuzzyScore(resolver.apply(item), keyword);
+ }
+
+ static class FuzzySearchGeneralDTO {
+ private final PreferenceSearchItem item;
+ private final float score;
+
+ FuzzySearchGeneralDTO(
+ final PreferenceSearchItem item,
+ final String keyword) {
+ this.item = item;
+ this.score = FUZZY_SCORE.fuzzyScore(
+ TextUtils.join(";", item.getAllRelevantSearchFields()),
+ keyword);
+ }
+
+ public PreferenceSearchItem getItem() {
+ return item;
+ }
+
+ public float getScore() {
+ return score;
+ }
+ }
+
+ static class FuzzySearchSpecificDTO {
+ private static final Map, Float> WEIGHT_MAP = Map.of(
+ // The user will most likely look for the title -> prioritize it
+ PreferenceSearchItem::getTitle, 1.5f,
+ // The summary is also important as it usually contains a larger desc
+ // Example: Searching for '4k' → 'show higher resolution' is shown
+ PreferenceSearchItem::getSummary, 1f,
+ // Entries are also important as they provide all known/possible values
+ // Example: Searching where the resolution can be changed to 720p
+ PreferenceSearchItem::getEntries, 1f
+ );
+
+ private final PreferenceSearchItem item;
+ private final float score;
+
+ FuzzySearchSpecificDTO(
+ final PreferenceSearchItem item,
+ final String keyword) {
+ this.item = item;
+
+ float attributeScoreSum = 0;
+ int countOfAttributesWithScore = 0;
+ for (final Map.Entry, Float> we
+ : WEIGHT_MAP.entrySet()) {
+ final String valueToProcess = we.getKey().apply(item);
+ if (valueToProcess.isEmpty()) {
+ continue;
+ }
+
+ attributeScoreSum +=
+ FUZZY_SCORE.fuzzyScore(valueToProcess, keyword) * we.getValue();
+ countOfAttributesWithScore++;
+ }
+
+ if (countOfAttributesWithScore != 0) {
+ this.score = attributeScoreSum / countOfAttributesWithScore;
+ } else {
+ this.score = 0;
+ }
+ }
+
+ public PreferenceSearchItem getItem() {
+ return item;
+ }
+
+ public float getScore() {
+ return score;
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
index b4d1c8985..37b98035a 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
@@ -21,16 +21,7 @@ public class PreferenceSearchConfiguration {
private BinaryOperator breadcrumbConcat =
(s1, s2) -> TextUtils.isEmpty(s1) ? s2 : (s1 + " > " + s2);
- private PreferenceSearchFunction searcher =
- (itemStream, keyword) ->
- itemStream
- // Filter the items by the keyword
- .filter(item -> item.getAllRelevantSearchFields().stream()
- .filter(str -> !TextUtils.isEmpty(str))
- .anyMatch(str ->
- str.toLowerCase().contains(keyword.toLowerCase())))
- // Limit the search results
- .limit(100);
+ private PreferenceSearchFunction searcher = new PreferenceFuzzySearchFunction();
private final List parserIgnoreElements = Arrays.asList(
PreferenceCategory.class.getSimpleName());
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
index a90d1084e..d9a17da16 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
@@ -81,7 +81,7 @@ public class PreferenceSearchFragment extends Fragment {
adapter.setContent(new ArrayList<>(results));
- setEmptyViewShown(!TextUtils.isEmpty(keyword) && results.isEmpty());
+ setEmptyViewShown(results.isEmpty());
}
private void setEmptyViewShown(final boolean shown) {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/similarity/FuzzyScore.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/similarity/FuzzyScore.java
new file mode 100644
index 000000000..4ab6bf60a
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/similarity/FuzzyScore.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.schabi.newpipe.settings.preferencesearch.similarity;
+
+import java.util.Locale;
+
+/**
+ * A matching algorithm that is similar to the searching algorithms implemented in editors such
+ * as Sublime Text, TextMate, Atom and others.
+ *
+ *
+ * One point is given for every matched character. Subsequent matches yield two bonus points.
+ * A higher score indicates a higher similarity.
+ *
+ *
+ *
+ * This code has been adapted from Apache Commons Lang 3.3.
+ *
+ *
+ * @since 1.0
+ *
+ * Note: This class was forked from
+ *
+ * apache/commons-text (8cfdafc) FuzzyScore.java
+ *
+ */
+public class FuzzyScore {
+
+ /**
+ * Locale used to change the case of text.
+ */
+ private final Locale locale;
+
+
+ /**
+ * This returns a {@link Locale}-specific {@link FuzzyScore}.
+ *
+ * @param locale The string matching logic is case insensitive.
+ A {@link Locale} is necessary to normalize both Strings to lower case.
+ * @throws IllegalArgumentException
+ * This is thrown if the {@link Locale} parameter is {@code null}.
+ */
+ public FuzzyScore(final Locale locale) {
+ if (locale == null) {
+ throw new IllegalArgumentException("Locale must not be null");
+ }
+ this.locale = locale;
+ }
+
+ /**
+ * Find the Fuzzy Score which indicates the similarity score between two
+ * Strings.
+ *
+ *
+ * score.fuzzyScore(null, null) = IllegalArgumentException
+ * score.fuzzyScore("not null", null) = IllegalArgumentException
+ * score.fuzzyScore(null, "not null") = IllegalArgumentException
+ * score.fuzzyScore("", "") = 0
+ * score.fuzzyScore("Workshop", "b") = 0
+ * score.fuzzyScore("Room", "o") = 1
+ * score.fuzzyScore("Workshop", "w") = 1
+ * score.fuzzyScore("Workshop", "ws") = 2
+ * score.fuzzyScore("Workshop", "wo") = 4
+ * score.fuzzyScore("Apache Software Foundation", "asf") = 3
+ *
+ *
+ * @param term a full term that should be matched against, must not be null
+ * @param query the query that will be matched against a term, must not be
+ * null
+ * @return result score
+ * @throws IllegalArgumentException if the term or query is {@code null}
+ */
+ public Integer fuzzyScore(final CharSequence term, final CharSequence query) {
+ if (term == null || query == null) {
+ throw new IllegalArgumentException("CharSequences must not be null");
+ }
+
+ // fuzzy logic is case insensitive. We normalize the Strings to lower
+ // case right from the start. Turning characters to lower case
+ // via Character.toLowerCase(char) is unfortunately insufficient
+ // as it does not accept a locale.
+ final String termLowerCase = term.toString().toLowerCase(locale);
+ final String queryLowerCase = query.toString().toLowerCase(locale);
+
+ // the resulting score
+ int score = 0;
+
+ // the position in the term which will be scanned next for potential
+ // query character matches
+ int termIndex = 0;
+
+ // index of the previously matched character in the term
+ int previousMatchingCharacterIndex = Integer.MIN_VALUE;
+
+ for (int queryIndex = 0; queryIndex < queryLowerCase.length(); queryIndex++) {
+ final char queryChar = queryLowerCase.charAt(queryIndex);
+
+ boolean termCharacterMatchFound = false;
+ for (; termIndex < termLowerCase.length()
+ && !termCharacterMatchFound; termIndex++) {
+ final char termChar = termLowerCase.charAt(termIndex);
+
+ if (queryChar == termChar) {
+ // simple character matches result in one point
+ score++;
+
+ // subsequent character matches further improve
+ // the score.
+ if (previousMatchingCharacterIndex + 1 == termIndex) {
+ score += 2;
+ }
+
+ previousMatchingCharacterIndex = termIndex;
+
+ // we can leave the nested loop. Every character in the
+ // query can match at most one character in the term.
+ termCharacterMatchFound = true;
+ }
+ }
+ }
+
+ return score;
+ }
+
+ /**
+ * Gets the locale.
+ *
+ * @return The locale
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+}
From 0f45c69388a2da0a465dff19813ec0e09face67f Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sun, 26 Dec 2021 23:46:22 +0100
Subject: [PATCH 11/27] Code cleanup + indexing improvements
* Removed unused method
* Only index all settings once -> Saves performance
* Fixed some SonarLint reported problems
---
.../newpipe/settings/SettingsActivity.java | 23 +++++++++-----
.../PreferenceFuzzySearchFunction.java | 12 +-------
.../preferencesearch/PreferenceParser.java | 4 +--
.../PreferenceSearchConfiguration.java | 2 +-
.../PreferenceSearchFragment.java | 30 ++++---------------
.../preferencesearch/PreferenceSearcher.java | 9 +++---
6 files changed, 29 insertions(+), 51 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 1f917e771..92bc21c9d 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -26,11 +26,13 @@ import org.schabi.newpipe.CheckForNewAppVersion;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.SettingsLayoutBinding;
+import org.schabi.newpipe.settings.preferencesearch.PreferenceParser;
import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchConfiguration;
import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchFragment;
import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchItem;
import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchResultHighlighter;
import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchResultListener;
+import org.schabi.newpipe.settings.preferencesearch.PreferenceSearcher;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.KeyboardUtil;
import org.schabi.newpipe.util.ThemeHelper;
@@ -58,9 +60,8 @@ import java.util.concurrent.TimeUnit;
* along with NewPipe. If not, see .
*/
-public class SettingsActivity extends AppCompatActivity
- implements
- BasePreferenceFragment.OnPreferenceStartFragmentCallback,
+public class SettingsActivity extends AppCompatActivity implements
+ PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,
PreferenceSearchResultListener {
private static final String TAG = "SettingsActivity";
private static final boolean DEBUG = MainActivity.DEBUG;
@@ -165,6 +166,7 @@ public class SettingsActivity extends AppCompatActivity
@Override
protected void onDestroy() {
setMenuSearchItem(null);
+ searchFragment = null;
super.onDestroy();
}
@@ -183,26 +185,33 @@ public class SettingsActivity extends AppCompatActivity
RxTextView.textChanges(searchEditText)
// Wait some time after the last input before actually searching
.debounce(200, TimeUnit.MILLISECONDS)
- .subscribe(v -> runOnUiThread(() -> onSearchChanged()));
+ .subscribe(v -> runOnUiThread(this::onSearchChanged));
// Configure clear button
searchContainer.findViewById(R.id.toolbar_search_clear)
.setOnClickListener(ev -> resetSearchText());
- // Build search configuration using SettingsResourceRegistry
prepareSearchConfig();
+ // Build search configuration using SettingsResourceRegistry
final PreferenceSearchConfiguration config = new PreferenceSearchConfiguration();
SettingsResourceRegistry.getInstance().getAllEntries().stream()
.filter(SettingsResourceRegistry.SettingRegistryEntry::isSearchable)
.map(SettingsResourceRegistry.SettingRegistryEntry::getPreferencesResId)
.forEach(config::index);
- searchFragment = new PreferenceSearchFragment(config);
+ // Build search items
+ final PreferenceParser parser = new PreferenceParser(getApplicationContext(), config);
+ final PreferenceSearcher searcher = new PreferenceSearcher(config);
+ config.getFiles().stream()
+ .map(parser::parse)
+ .forEach(searcher::add);
+
+ searchFragment = new PreferenceSearchFragment(searcher);
}
private void prepareSearchConfig() {
- // Check if the update settings should be available
+ // Check if the update settings are available
if (!CheckForNewAppVersion.isReleaseApk(App.getApp())) {
SettingsResourceRegistry.getInstance()
.getEntryByPreferencesResId(R.xml.update_settings)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java
index 48f507a41..88ef790c8 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java
@@ -2,8 +2,6 @@ package org.schabi.newpipe.settings.preferencesearch;
import android.text.TextUtils;
-import androidx.annotation.NonNull;
-
import org.schabi.newpipe.settings.preferencesearch.similarity.FuzzyScore;
import java.util.Comparator;
@@ -22,7 +20,7 @@ public class PreferenceFuzzySearchFunction
final Stream allAvailable,
final String keyword
) {
- final float maxScore = (keyword.length() + 1) * 3 - 2; // First can't get +2 bonus score
+ final int maxScore = (keyword.length() + 1) * 3 - 2; // First can't get +2 bonus score
return allAvailable
// General search
@@ -39,14 +37,6 @@ public class PreferenceFuzzySearchFunction
.limit(20);
}
- private float computeFuzzyScore(
- @NonNull final PreferenceSearchItem item,
- @NonNull final Function resolver,
- @NonNull final String keyword
- ) {
- return FUZZY_SCORE.fuzzyScore(resolver.apply(item), keyword);
- }
-
static class FuzzySearchGeneralDTO {
private final PreferenceSearchItem item;
private final float score;
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
index 1cf401892..b52daeb5b 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
@@ -18,7 +18,7 @@ import java.util.Objects;
/**
* Parses the corresponding preference-file(s).
*/
-class PreferenceParser {
+public class PreferenceParser {
private static final String TAG = "PreferenceParser";
private static final String NS_ANDROID = "http://schemas.android.com/apk/res/android";
@@ -28,7 +28,7 @@ class PreferenceParser {
private final Map allPreferences;
private final PreferenceSearchConfiguration searchConfiguration;
- PreferenceParser(
+ public PreferenceParser(
final Context context,
final PreferenceSearchConfiguration searchConfiguration
) {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
index 37b98035a..1c9dc99aa 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
@@ -50,7 +50,7 @@ public class PreferenceSearchConfiguration {
return item;
}
- List getFiles() {
+ public List getFiles() {
return itemsToIndex;
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
index d9a17da16..1b1d627c1 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
@@ -24,29 +24,15 @@ import java.util.Objects;
public class PreferenceSearchFragment extends Fragment {
public static final String NAME = PreferenceSearchFragment.class.getSimpleName();
- private final PreferenceSearchConfiguration searchConfiguration;
-
private final PreferenceSearcher searcher;
+
private SearchViewHolder viewHolder;
private PreferenceSearchAdapter adapter;
- public PreferenceSearchFragment(final PreferenceSearchConfiguration searchConfiguration) {
- this.searchConfiguration = searchConfiguration;
- this.searcher = new PreferenceSearcher(searchConfiguration);
- }
-
- @Override
- public void onCreate(@Nullable final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final PreferenceParser parser =
- new PreferenceParser(
- getContext(),
- searchConfiguration);
-
- searchConfiguration.getFiles().stream()
- .map(parser::parse)
- .forEach(searcher::add);
+ public PreferenceSearchFragment(
+ final PreferenceSearcher searcher
+ ) {
+ this.searcher = searcher;
}
@Nullable
@@ -98,12 +84,6 @@ public class PreferenceSearchFragment extends Fragment {
((PreferenceSearchResultListener) getActivity()).onSearchResultClicked(item);
}
- @Override
- public void onDestroy() {
- searcher.close();
- super.onDestroy();
- }
-
private static class SearchViewHolder {
private final RecyclerView recyclerView;
private final View emptyStateView;
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java
index f9427a1ca..87dd11693 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java
@@ -6,16 +6,16 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
-class PreferenceSearcher implements AutoCloseable {
+public class PreferenceSearcher {
private final List allEntries = new ArrayList<>();
private final PreferenceSearchConfiguration configuration;
- PreferenceSearcher(final PreferenceSearchConfiguration configuration) {
+ public PreferenceSearcher(final PreferenceSearchConfiguration configuration) {
this.configuration = configuration;
}
- void add(final List items) {
+ public void add(final List items) {
allEntries.addAll(items);
}
@@ -29,8 +29,7 @@ class PreferenceSearcher implements AutoCloseable {
.collect(Collectors.toList());
}
- @Override
- public void close() {
+ public void clear() {
allEntries.clear();
}
}
From d59314801c035ea1292e5c685437ff0b09982c54 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Mon, 27 Dec 2021 18:57:17 +0100
Subject: [PATCH 12/27] Code rework
---
.../preferencesearch/PreferenceParser.java | 4 +--
.../PreferenceSearchItem.java | 33 ++++++++++++-------
2 files changed, 24 insertions(+), 13 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
index b52daeb5b..afd2c1b4f 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
@@ -5,6 +5,7 @@ import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.XmlRes;
import androidx.preference.PreferenceManager;
import org.xmlpull.v1.XmlPullParser;
@@ -111,7 +112,7 @@ public class PreferenceParser {
private PreferenceSearchItem parseSearchResult(
final XmlPullParser xpp,
final String breadcrumbs,
- final int searchIndexItemResId
+ @XmlRes final int searchIndexItemResId
) {
final String key = readString(getAttribute(xpp, "key"));
final String[] entries = readStringArray(getAttribute(xpp, "entries"));
@@ -130,7 +131,6 @@ public class PreferenceParser {
entries,
entryValues),
TextUtils.join(",", entries),
- readString(getAttribute(xpp, NS_SEARCH, "keywords")),
breadcrumbs,
searchIndexItemResId
);
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java
index 3030a78bb..52935ef8e 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java
@@ -1,6 +1,7 @@
package org.schabi.newpipe.settings.preferencesearch;
import androidx.annotation.NonNull;
+import androidx.annotation.XmlRes;
import java.util.Arrays;
import java.util.List;
@@ -10,18 +11,35 @@ import java.util.Objects;
* Represents a preference-item inside the search.
*/
public class PreferenceSearchItem {
+ /**
+ * Key of the setting/preference. E.g. used inside {@link android.content.SharedPreferences}.
+ */
@NonNull
private final String key;
+ /**
+ * Title of the setting, e.g. 'Default resolution' or 'Show higher resolutions'.
+ */
@NonNull
private final String title;
+ /**
+ * Summary of the setting, e.g. '480p' or 'Only some devices can play 2k/4k'.
+ */
@NonNull
private final String summary;
+ /**
+ * Possible entries of the setting, e.g. 480p,720p,...
+ */
@NonNull
private final String entries;
- @NonNull
- private final String keywords;
+ /**
+ * Breadcrumbs - a hint where the setting is located e.g. 'Video and Audio > Player'
+ */
@NonNull
private final String breadcrumbs;
+ /**
+ * The xml-resource where this item was found/built from.
+ */
+ @XmlRes
private final int searchIndexItemResId;
public PreferenceSearchItem(
@@ -29,15 +47,13 @@ public class PreferenceSearchItem {
@NonNull final String title,
@NonNull final String summary,
@NonNull final String entries,
- @NonNull final String keywords,
@NonNull final String breadcrumbs,
- final int searchIndexItemResId
+ @XmlRes final int searchIndexItemResId
) {
this.key = Objects.requireNonNull(key);
this.title = Objects.requireNonNull(title);
this.summary = Objects.requireNonNull(summary);
this.entries = Objects.requireNonNull(entries);
- this.keywords = Objects.requireNonNull(keywords);
this.breadcrumbs = Objects.requireNonNull(breadcrumbs);
this.searchIndexItemResId = searchIndexItemResId;
}
@@ -62,10 +78,6 @@ public class PreferenceSearchItem {
return breadcrumbs;
}
- public String getKeywords() {
- return keywords;
- }
-
public int getSearchIndexItemResId() {
return searchIndexItemResId;
}
@@ -79,8 +91,7 @@ public class PreferenceSearchItem {
getTitle(),
getSummary(),
getEntries(),
- getBreadcrumbs(),
- getKeywords());
+ getBreadcrumbs());
}
From 6b23df06592ccbe52534d43448310125685c9eaa Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Wed, 29 Dec 2021 18:12:33 +0100
Subject: [PATCH 13/27] Made debug settings searchable (debug only)
* Consolidated main-setttings into a single file
* Debug settings are only enabled in the DEBUG build
* Moved LeakCanary (debug) specific stuff into a small class that's only shipped with the debug build
* Other minor fixes
---
.../settings/DebugSettingsFragment.java | 50 ++++++++++++++++---
.../settings/MainSettingsFragment.java | 12 +++--
.../newpipe/settings/SettingsActivity.java | 7 +++
.../settings/SettingsResourceRegistry.java | 5 +-
app/src/main/res/values/strings.xml | 1 +
.../{debug => main}/res/xml/main_settings.xml | 0
app/src/release/res/xml/main_settings.xml | 49 ------------------
7 files changed, 61 insertions(+), 63 deletions(-)
rename app/src/{debug => main}/java/org/schabi/newpipe/settings/DebugSettingsFragment.java (54%)
rename app/src/{debug => main}/res/xml/main_settings.xml (100%)
delete mode 100644 app/src/release/res/xml/main_settings.xml
diff --git a/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
similarity index 54%
rename from app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
rename to app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
index f48be553f..710a440e1 100644
--- a/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
@@ -1,5 +1,6 @@
package org.schabi.newpipe.settings;
+import android.content.Intent;
import android.os.Bundle;
import androidx.preference.Preference;
@@ -10,13 +11,15 @@ import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.util.PicassoHelper;
-import leakcanary.LeakCanary;
+import java.util.Optional;
public class DebugSettingsFragment extends BasePreferenceFragment {
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
- addPreferencesFromResource(R.xml.debug_settings);
+ addPreferencesFromResourceRegistry();
+ final Preference allowHeapDumpingPreference
+ = findPreference(getString(R.string.allow_heap_dumping_key));
final Preference showMemoryLeaksPreference
= findPreference(getString(R.string.show_memory_leaks_key));
final Preference showImageIndicatorsPreference
@@ -28,16 +31,29 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
final Preference createErrorNotificationPreference
= findPreference(getString(R.string.create_error_notification_key));
+ assert allowHeapDumpingPreference != null;
assert showMemoryLeaksPreference != null;
assert showImageIndicatorsPreference != null;
assert crashTheAppPreference != null;
assert showErrorSnackbarPreference != null;
assert createErrorNotificationPreference != null;
- showMemoryLeaksPreference.setOnPreferenceClickListener(preference -> {
- startActivity(LeakCanary.INSTANCE.newLeakDisplayActivityIntent());
- return true;
- });
+ final Optional optPDLeakCanary = getBVLeakCanary();
+
+ allowHeapDumpingPreference.setEnabled(optPDLeakCanary.isPresent());
+ showMemoryLeaksPreference.setEnabled(optPDLeakCanary.isPresent());
+
+ if (optPDLeakCanary.isPresent()) {
+ final DebugSettingsBVLeakCanaryAPI pdLeakCanary = optPDLeakCanary.get();
+
+ showMemoryLeaksPreference.setOnPreferenceClickListener(preference -> {
+ startActivity(pdLeakCanary.getNewLeakDisplayActivityIntent());
+ return true;
+ });
+ } else {
+ allowHeapDumpingPreference.setSummary(R.string.leak_canary_not_available);
+ showMemoryLeaksPreference.setSummary(R.string.leak_canary_not_available);
+ }
showImageIndicatorsPreference.setOnPreferenceChangeListener((preference, newValue) -> {
PicassoHelper.setIndicatorsEnabled((Boolean) newValue);
@@ -60,4 +76,26 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
return true;
});
}
+
+ private Optional getBVLeakCanary() {
+ try {
+ // Try to find the implementation of the LeakCanary API
+ return Optional.of((DebugSettingsBVLeakCanaryAPI)
+ Class.forName(DebugSettingsBVLeakCanaryAPI.IMPL_CLASS).newInstance());
+ } catch (final ClassNotFoundException
+ | IllegalAccessException | java.lang.InstantiationException e) {
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * Build variant dependent leak canary API for this fragment.
+ * Why is LeakCanary not used directly? Because it can't be assured
+ */
+ public interface DebugSettingsBVLeakCanaryAPI {
+ String IMPL_CLASS =
+ "org.schabi.newpipe.settings.DebugSettingsBVLeakCanary";
+
+ Intent getNewLeakDisplayActivityIntent();
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java
index 6cd165861..d7fb559d6 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java
@@ -6,7 +6,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import androidx.annotation.NonNull;
-import androidx.preference.Preference;
import org.schabi.newpipe.App;
import org.schabi.newpipe.CheckForNewAppVersion;
@@ -26,12 +25,17 @@ public class MainSettingsFragment extends BasePreferenceFragment {
// Check if the app is updatable
if (!CheckForNewAppVersion.isReleaseApk(App.getApp())) {
- final Preference update
- = findPreference(getString(R.string.update_pref_screen_key));
- getPreferenceScreen().removePreference(update);
+ getPreferenceScreen().removePreference(
+ findPreference(getString(R.string.update_pref_screen_key)));
defaultPreferences.edit().putBoolean(getString(R.string.update_app_key), false).apply();
}
+
+ // Hide debug preferences in RELEASE build variant
+ if (!DEBUG) {
+ getPreferenceScreen().removePreference(
+ findPreference(getString(R.string.debug_pref_screen_key)));
+ }
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 92bc21c9d..25d22dc9f 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -217,6 +217,13 @@ public class SettingsActivity extends AppCompatActivity implements
.getEntryByPreferencesResId(R.xml.update_settings)
.setSearchable(false);
}
+
+ // Hide debug preferences in RELEASE build variant
+ if (DEBUG) {
+ SettingsResourceRegistry.getInstance()
+ .getEntryByPreferencesResId(R.xml.debug_settings)
+ .setSearchable(true);
+ }
}
public void setMenuSearchItem(final MenuItem menuSearchItem) {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java
index c4db9f93d..c4751abea 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java
@@ -1,7 +1,6 @@
package org.schabi.newpipe.settings;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.XmlRes;
import androidx.fragment.app.Fragment;
@@ -34,6 +33,7 @@ public final class SettingsResourceRegistry {
add(AppearanceSettingsFragment.class, R.xml.appearance_settings);
add(ContentSettingsFragment.class, R.xml.content_settings);
+ add(DebugSettingsFragment.class, R.xml.debug_settings).setSearchable(false);
add(DownloadSettingsFragment.class, R.xml.download_settings);
add(HistorySettingsFragment.class, R.xml.history_settings);
add(NotificationSettingsFragment.class, R.xml.notification_settings);
@@ -51,7 +51,6 @@ public final class SettingsResourceRegistry {
return entry;
}
- @Nullable
public SettingRegistryEntry getEntryByFragmentClass(
final Class extends Fragment> fragmentClass
) {
@@ -62,7 +61,6 @@ public final class SettingsResourceRegistry {
.orElse(null);
}
- @Nullable
public SettingRegistryEntry getEntryByPreferencesResId(@XmlRes final int preferencesResId) {
return registeredEntries.stream()
.filter(e -> Objects.equals(e.getPreferencesResId(), preferencesResId))
@@ -78,7 +76,6 @@ public final class SettingsResourceRegistry {
return entry.getPreferencesResId();
}
- @Nullable
public Class extends Fragment> getFragmentClass(@XmlRes final int preferencesResId) {
final SettingRegistryEntry entry = getEntryByPreferencesResId(preferencesResId);
if (entry == null) {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f7ad41ba8..64ed6980b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -441,6 +441,7 @@
Captions
Modify player caption text scale and background styles. Requires app restart to take effect
+ LeakCanary is not available
Memory leak monitoring may cause the app to become unresponsive when heap dumping
Show memory leaks
Report out-of-lifecycle errors
diff --git a/app/src/debug/res/xml/main_settings.xml b/app/src/main/res/xml/main_settings.xml
similarity index 100%
rename from app/src/debug/res/xml/main_settings.xml
rename to app/src/main/res/xml/main_settings.xml
diff --git a/app/src/release/res/xml/main_settings.xml b/app/src/release/res/xml/main_settings.xml
deleted file mode 100644
index 1d5241102..000000000
--- a/app/src/release/res/xml/main_settings.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 658168eb8d97e54ac23f1dfdccb7fd8ec2786035 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Wed, 29 Dec 2021 18:29:46 +0100
Subject: [PATCH 14/27] Fixed some sonar warnings
---
.../newpipe/settings/DebugSettingsFragment.java | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
index 710a440e1..915531779 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
@@ -11,9 +11,12 @@ import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.util.PicassoHelper;
+import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
public class DebugSettingsFragment extends BasePreferenceFragment {
+ private static final String DUMMY = "Dummy";
+
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
addPreferencesFromResourceRegistry();
@@ -61,18 +64,18 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
});
crashTheAppPreference.setOnPreferenceClickListener(preference -> {
- throw new RuntimeException();
+ throw new RuntimeException(DUMMY);
});
showErrorSnackbarPreference.setOnPreferenceClickListener(preference -> {
ErrorUtil.showUiErrorSnackbar(DebugSettingsFragment.this,
- "Dummy", new RuntimeException("Dummy"));
+ DUMMY, new RuntimeException(DUMMY));
return true;
});
createErrorNotificationPreference.setOnPreferenceClickListener(preference -> {
ErrorUtil.createNotification(requireContext(),
- new ErrorInfo(new RuntimeException("Dummy"), UserAction.UI_ERROR, "Dummy"));
+ new ErrorInfo(new RuntimeException(DUMMY), UserAction.UI_ERROR, DUMMY));
return true;
});
}
@@ -81,9 +84,10 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
try {
// Try to find the implementation of the LeakCanary API
return Optional.of((DebugSettingsBVLeakCanaryAPI)
- Class.forName(DebugSettingsBVLeakCanaryAPI.IMPL_CLASS).newInstance());
- } catch (final ClassNotFoundException
- | IllegalAccessException | java.lang.InstantiationException e) {
+ Class.forName(DebugSettingsBVLeakCanaryAPI.IMPL_CLASS)
+ .getDeclaredConstructor()
+ .newInstance());
+ } catch (final Exception e) {
return Optional.empty();
}
}
From bebd2b449cbf59bfe721a2c4b825eac9243051c0 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Wed, 29 Dec 2021 18:46:45 +0100
Subject: [PATCH 15/27] Removed unused import
---
.../java/org/schabi/newpipe/settings/DebugSettingsFragment.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
index 915531779..a42d47313 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
@@ -11,7 +11,6 @@ import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.util.PicassoHelper;
-import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
public class DebugSettingsFragment extends BasePreferenceFragment {
From c5a06243a6ed6982bcac663f40543df820803402 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Wed, 29 Dec 2021 18:49:37 +0100
Subject: [PATCH 16/27] Fixed variable name
---
.../schabi/newpipe/settings/DebugSettingsFragment.java | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
index a42d47313..3e28a6065 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
@@ -40,13 +40,13 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
assert showErrorSnackbarPreference != null;
assert createErrorNotificationPreference != null;
- final Optional optPDLeakCanary = getBVLeakCanary();
+ final Optional optBVLeakCanary = getBVLeakCanary();
- allowHeapDumpingPreference.setEnabled(optPDLeakCanary.isPresent());
- showMemoryLeaksPreference.setEnabled(optPDLeakCanary.isPresent());
+ allowHeapDumpingPreference.setEnabled(optBVLeakCanary.isPresent());
+ showMemoryLeaksPreference.setEnabled(optBVLeakCanary.isPresent());
- if (optPDLeakCanary.isPresent()) {
- final DebugSettingsBVLeakCanaryAPI pdLeakCanary = optPDLeakCanary.get();
+ if (optBVLeakCanary.isPresent()) {
+ final DebugSettingsBVLeakCanaryAPI pdLeakCanary = optBVLeakCanary.get();
showMemoryLeaksPreference.setOnPreferenceClickListener(preference -> {
startActivity(pdLeakCanary.getNewLeakDisplayActivityIntent());
From 8bbc3e531c591539ab89ceac42c94586c130a94a Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Wed, 29 Dec 2021 22:17:30 +0100
Subject: [PATCH 17/27] Fixed gitignore and commited missing file
---
.gitignore | 4 ++--
.../settings/DebugSettingsBVLeakCanary.java | 15 +++++++++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
create mode 100644 app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVLeakCanary.java
diff --git a/.gitignore b/.gitignore
index 40e7d2c03..1352b6917 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,8 +8,8 @@ captures/
*~
.weblate
*.class
-**/debug/
-**/release/
+app/debug/
+app/release/
# vscode / eclipse files
*.classpath
diff --git a/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVLeakCanary.java b/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVLeakCanary.java
new file mode 100644
index 000000000..f8fd7f969
--- /dev/null
+++ b/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVLeakCanary.java
@@ -0,0 +1,15 @@
+package org.schabi.newpipe.settings;
+
+import android.content.Intent;
+
+import leakcanary.LeakCanary;
+
+@SuppressWarnings("unused") // Class is used but loaded via reflection
+public class DebugSettingsBVLeakCanary
+ implements DebugSettingsFragment.DebugSettingsBVLeakCanaryAPI {
+
+ @Override
+ public Intent getNewLeakDisplayActivityIntent() {
+ return LeakCanary.INSTANCE.newLeakDisplayActivityIntent();
+ }
+}
From ce4dd33eab241ed79233a44706fb0ce00ba24dd1 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 31 Dec 2021 14:50:27 +0100
Subject: [PATCH 18/27] Fixed problems with Android's lifecycle (restore)
---
.../newpipe/settings/SettingsActivity.java | 84 +++++++++++++++----
.../PreferenceSearchFragment.java | 8 +-
2 files changed, 73 insertions(+), 19 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 25d22dc9f..4dfbf6c08 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -3,6 +3,7 @@ package org.schabi.newpipe.settings;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
import android.os.Bundle;
+import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -40,6 +41,9 @@ import org.schabi.newpipe.views.FocusOverlayView;
import java.util.concurrent.TimeUnit;
+import icepick.Icepick;
+import icepick.State;
+
/*
* Created by Christian Schabesberger on 31.08.15.
*
@@ -77,20 +81,37 @@ public class SettingsActivity extends AppCompatActivity implements
private View searchContainer;
private EditText searchEditText;
+ // State
+ @State
+ String searchText;
+ @State
+ boolean wasSearchActive;
+
@Override
protected void onCreate(final Bundle savedInstanceBundle) {
setTheme(ThemeHelper.getSettingsThemeStyle(this));
assureCorrectAppLanguage(this);
+
super.onCreate(savedInstanceBundle);
+ Icepick.restoreInstanceState(this, savedInstanceBundle);
+ final boolean restored = savedInstanceBundle != null;
final SettingsLayoutBinding settingsLayoutBinding =
SettingsLayoutBinding.inflate(getLayoutInflater());
setContentView(settingsLayoutBinding.getRoot());
- initSearch(settingsLayoutBinding);
+ initSearch(settingsLayoutBinding, restored);
setSupportActionBar(settingsLayoutBinding.settingsToolbarLayout.toolbar);
- if (savedInstanceBundle == null) {
+ if (restored) {
+ // Restore state
+ if (this.wasSearchActive) {
+ setSearchActive(true);
+ if (!TextUtils.isEmpty(this.searchText)) {
+ this.searchEditText.setText(this.searchText);
+ }
+ }
+ } else {
getSupportFragmentManager().beginTransaction()
.replace(R.id.settings_fragment_holder, new MainSettingsFragment())
.commit();
@@ -101,6 +122,12 @@ public class SettingsActivity extends AppCompatActivity implements
}
}
+ @Override
+ protected void onSaveInstanceState(@NonNull final Bundle outState) {
+ super.onSaveInstanceState(outState);
+ Icepick.saveInstanceState(this, outState);
+ }
+
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
final ActionBar actionBar = getSupportActionBar();
@@ -175,7 +202,10 @@ public class SettingsActivity extends AppCompatActivity implements
//////////////////////////////////////////////////////////////////////////*/
//region Search
- private void initSearch(final SettingsLayoutBinding settingsLayoutBinding) {
+ private void initSearch(
+ final SettingsLayoutBinding settingsLayoutBinding,
+ final boolean restored
+ ) {
searchContainer =
settingsLayoutBinding.settingsToolbarLayout.toolbar
.findViewById(R.id.toolbar_search_container);
@@ -207,7 +237,19 @@ public class SettingsActivity extends AppCompatActivity implements
.map(parser::parse)
.forEach(searcher::add);
- searchFragment = new PreferenceSearchFragment(searcher);
+ if (restored) {
+ searchFragment = (PreferenceSearchFragment) getSupportFragmentManager()
+ .findFragmentByTag(PreferenceSearchFragment.NAME);
+ if (searchFragment != null) {
+ // Hide/Remove the search fragment otherwise we get an exception
+ // when adding it (because it's already present)
+ hideSearchFragment();
+ }
+ }
+ if (searchFragment == null) {
+ searchFragment = new PreferenceSearchFragment();
+ }
+ searchFragment.setSearcher(searcher);
}
private void prepareSearchConfig() {
@@ -228,36 +270,45 @@ public class SettingsActivity extends AppCompatActivity implements
public void setMenuSearchItem(final MenuItem menuSearchItem) {
this.menuSearchItem = menuSearchItem;
+
+ // Ensure that the item is in the correct state when adding it. This is due to
+ // Android's lifecycle (the Activity is recreated before the Fragment that registers this)
+ if (menuSearchItem != null) {
+ menuSearchItem.setVisible(!isSearchActive());
+ }
}
public void setSearchActive(final boolean active) {
+ if (DEBUG) {
+ Log.d(TAG, "setSearchActive called active=" + active);
+ }
+
// Ignore if search is already in correct state
if (isSearchActive() == active) {
return;
}
- if (DEBUG) {
- Log.d(TAG, "setSearchActive called active=" + active);
- }
+ wasSearchActive = active;
searchContainer.setVisibility(active ? View.VISIBLE : View.GONE);
if (menuSearchItem != null) {
menuSearchItem.setVisible(!active);
}
- final FragmentManager fm = getSupportFragmentManager();
if (active) {
- fm.beginTransaction()
+ getSupportFragmentManager()
+ .beginTransaction()
.add(FRAGMENT_HOLDER_ID, searchFragment, PreferenceSearchFragment.NAME)
.addToBackStack(PreferenceSearchFragment.NAME)
.commit();
KeyboardUtil.showKeyboard(this, searchEditText);
} else if (searchFragment != null) {
- fm.beginTransaction().remove(searchFragment).commit();
- fm.popBackStack(
- PreferenceSearchFragment.NAME,
- FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ hideSearchFragment();
+ getSupportFragmentManager()
+ .popBackStack(
+ PreferenceSearchFragment.NAME,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
KeyboardUtil.hideKeyboard(this, searchEditText);
}
@@ -265,6 +316,10 @@ public class SettingsActivity extends AppCompatActivity implements
resetSearchText();
}
+ private void hideSearchFragment() {
+ getSupportFragmentManager().beginTransaction().remove(searchFragment).commit();
+ }
+
private void resetSearchText() {
searchEditText.setText("");
}
@@ -279,7 +334,8 @@ public class SettingsActivity extends AppCompatActivity implements
}
if (searchFragment != null) {
- searchFragment.updateSearchResults(this.searchEditText.getText().toString());
+ searchText = this.searchEditText.getText().toString();
+ searchFragment.updateSearchResults(searchText);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
index 1b1d627c1..38c87ea76 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
@@ -24,14 +24,12 @@ import java.util.Objects;
public class PreferenceSearchFragment extends Fragment {
public static final String NAME = PreferenceSearchFragment.class.getSimpleName();
- private final PreferenceSearcher searcher;
+ private PreferenceSearcher searcher;
private SearchViewHolder viewHolder;
private PreferenceSearchAdapter adapter;
- public PreferenceSearchFragment(
- final PreferenceSearcher searcher
- ) {
+ public void setSearcher(final PreferenceSearcher searcher) {
this.searcher = searcher;
}
@@ -56,7 +54,7 @@ public class PreferenceSearchFragment extends Fragment {
}
public void updateSearchResults(final String keyword) {
- if (adapter == null) {
+ if (adapter == null || searcher == null) {
return;
}
From 9b2c86a37be7ce57f5a26dd222f854be0479a771 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 31 Dec 2021 16:14:01 +0100
Subject: [PATCH 19/27] Improved documentation
---
.../settings/DebugSettingsBVDLeakCanary.java | 20 +++++++++++++++++++
.../settings/DebugSettingsBVLeakCanary.java | 15 --------------
.../settings/DebugSettingsFragment.java | 20 +++++++++++--------
3 files changed, 32 insertions(+), 23 deletions(-)
create mode 100644 app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVDLeakCanary.java
delete mode 100644 app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVLeakCanary.java
diff --git a/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVDLeakCanary.java b/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVDLeakCanary.java
new file mode 100644
index 000000000..a2d65f6f4
--- /dev/null
+++ b/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVDLeakCanary.java
@@ -0,0 +1,20 @@
+package org.schabi.newpipe.settings;
+
+import android.content.Intent;
+
+import leakcanary.LeakCanary;
+
+/**
+ * Build variant dependent (BVD) leak canary API implementation for the debug settings fragment.
+ * This class is loaded via reflection by
+ * {@link DebugSettingsFragment.DebugSettingsBVDLeakCanaryAPI}.
+ */
+@SuppressWarnings("unused") // Class is used but loaded via reflection
+public class DebugSettingsBVDLeakCanary
+ implements DebugSettingsFragment.DebugSettingsBVDLeakCanaryAPI {
+
+ @Override
+ public Intent getNewLeakDisplayActivityIntent() {
+ return LeakCanary.INSTANCE.newLeakDisplayActivityIntent();
+ }
+}
diff --git a/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVLeakCanary.java b/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVLeakCanary.java
deleted file mode 100644
index f8fd7f969..000000000
--- a/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVLeakCanary.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.schabi.newpipe.settings;
-
-import android.content.Intent;
-
-import leakcanary.LeakCanary;
-
-@SuppressWarnings("unused") // Class is used but loaded via reflection
-public class DebugSettingsBVLeakCanary
- implements DebugSettingsFragment.DebugSettingsBVLeakCanaryAPI {
-
- @Override
- public Intent getNewLeakDisplayActivityIntent() {
- return LeakCanary.INSTANCE.newLeakDisplayActivityIntent();
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
index 3e28a6065..395c7c0f0 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
@@ -40,13 +40,13 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
assert showErrorSnackbarPreference != null;
assert createErrorNotificationPreference != null;
- final Optional optBVLeakCanary = getBVLeakCanary();
+ final Optional optBVLeakCanary = getBVDLeakCanary();
allowHeapDumpingPreference.setEnabled(optBVLeakCanary.isPresent());
showMemoryLeaksPreference.setEnabled(optBVLeakCanary.isPresent());
if (optBVLeakCanary.isPresent()) {
- final DebugSettingsBVLeakCanaryAPI pdLeakCanary = optBVLeakCanary.get();
+ final DebugSettingsBVDLeakCanaryAPI pdLeakCanary = optBVLeakCanary.get();
showMemoryLeaksPreference.setOnPreferenceClickListener(preference -> {
startActivity(pdLeakCanary.getNewLeakDisplayActivityIntent());
@@ -79,11 +79,15 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
});
}
- private Optional getBVLeakCanary() {
+ /**
+ * Tries to find the {@link DebugSettingsBVDLeakCanaryAPI#IMPL_CLASS} and loads it if available.
+ * @return An {@link Optional} which is empty if the implementation class couldn't be loaded.
+ */
+ private Optional getBVDLeakCanary() {
try {
// Try to find the implementation of the LeakCanary API
- return Optional.of((DebugSettingsBVLeakCanaryAPI)
- Class.forName(DebugSettingsBVLeakCanaryAPI.IMPL_CLASS)
+ return Optional.of((DebugSettingsBVDLeakCanaryAPI)
+ Class.forName(DebugSettingsBVDLeakCanaryAPI.IMPL_CLASS)
.getDeclaredConstructor()
.newInstance());
} catch (final Exception e) {
@@ -92,12 +96,12 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
}
/**
- * Build variant dependent leak canary API for this fragment.
+ * Build variant dependent (BVD) leak canary API for this fragment.
* Why is LeakCanary not used directly? Because it can't be assured
*/
- public interface DebugSettingsBVLeakCanaryAPI {
+ public interface DebugSettingsBVDLeakCanaryAPI {
String IMPL_CLASS =
- "org.schabi.newpipe.settings.DebugSettingsBVLeakCanary";
+ "org.schabi.newpipe.settings.DebugSettingsBVDLeakCanary";
Intent getNewLeakDisplayActivityIntent();
}
From 37cd71328c943e7ccc88baa8ea17ff650d3c922a Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sun, 9 Jan 2022 14:38:36 +0100
Subject: [PATCH 20/27] Moved ``FuzzyScore`` to original Apache package
---
.../commons/text}/similarity/FuzzyScore.java | 2 +-
.../preferencesearch/PreferenceFuzzySearchFunction.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
rename app/src/main/java/org/{schabi/newpipe/settings/preferencesearch => apache/commons/text}/similarity/FuzzyScore.java (98%)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/similarity/FuzzyScore.java b/app/src/main/java/org/apache/commons/text/similarity/FuzzyScore.java
similarity index 98%
rename from app/src/main/java/org/schabi/newpipe/settings/preferencesearch/similarity/FuzzyScore.java
rename to app/src/main/java/org/apache/commons/text/similarity/FuzzyScore.java
index 4ab6bf60a..bbab7fd78 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/similarity/FuzzyScore.java
+++ b/app/src/main/java/org/apache/commons/text/similarity/FuzzyScore.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.schabi.newpipe.settings.preferencesearch.similarity;
+package org.apache.commons.text.similarity;
import java.util.Locale;
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java
index 88ef790c8..7c231cafb 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java
@@ -2,7 +2,7 @@ package org.schabi.newpipe.settings.preferencesearch;
import android.text.TextUtils;
-import org.schabi.newpipe.settings.preferencesearch.similarity.FuzzyScore;
+import org.apache.commons.text.similarity.FuzzyScore;
import java.util.Comparator;
import java.util.Locale;
From b16e972710e0bac6ce8cf7c9f56b3ef64a89ed7b Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sun, 9 Jan 2022 14:58:10 +0100
Subject: [PATCH 21/27] Improved doc
---
.../preferencesearch/PreferenceSearchResultHighlighter.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java
index e48ca28ce..418a3ea46 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java
@@ -29,9 +29,11 @@ public final class PreferenceSearchResultHighlighter {
/**
* Highlight the specified preference.
+ *
+ * Note: This function is Thread independent (can be called from outside of the main thread).
*
- * @param item
- * @param prefsFragment
+ * @param item The item to highlight
+ * @param prefsFragment The fragment where the items is located on
*/
public static void highlight(
final PreferenceSearchItem item,
From e2f449f0c8782e19ae2a9cf64936931b9e03a578 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sun, 9 Jan 2022 15:22:05 +0100
Subject: [PATCH 22/27] Code improvements
* Renamed methods so that they are more understandable
* Removed ``SearchIndexItem``
---
.../newpipe/settings/SettingsActivity.java | 25 +++--
.../preferencesearch/PreferenceParser.java | 13 +--
.../PreferenceSearchConfiguration.java | 99 +------------------
.../preferencesearch/PreferenceSearcher.java | 2 +-
4 files changed, 24 insertions(+), 115 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 4dfbf6c08..7d56863eb 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -171,6 +171,7 @@ public class SettingsActivity extends AppCompatActivity implements
@Override
public boolean onPreferenceStartFragment(final PreferenceFragmentCompat caller,
final Preference preference) {
+ preference.getExtras()
showSettingsFragment(instantiateFragment(preference.getFragment()));
return true;
}
@@ -221,20 +222,24 @@ public class SettingsActivity extends AppCompatActivity implements
searchContainer.findViewById(R.id.toolbar_search_clear)
.setOnClickListener(ev -> resetSearchText());
- prepareSearchConfig();
+ ensureSearchRepresentsApplicationState();
// Build search configuration using SettingsResourceRegistry
final PreferenceSearchConfiguration config = new PreferenceSearchConfiguration();
- SettingsResourceRegistry.getInstance().getAllEntries().stream()
- .filter(SettingsResourceRegistry.SettingRegistryEntry::isSearchable)
- .map(SettingsResourceRegistry.SettingRegistryEntry::getPreferencesResId)
- .forEach(config::index);
+
// Build search items
final PreferenceParser parser = new PreferenceParser(getApplicationContext(), config);
final PreferenceSearcher searcher = new PreferenceSearcher(config);
- config.getFiles().stream()
+
+ // Find all searchable SettingsResourceRegistry fragments
+ SettingsResourceRegistry.getInstance().getAllEntries().stream()
+ .filter(SettingsResourceRegistry.SettingRegistryEntry::isSearchable)
+ // Get the resId
+ .map(SettingsResourceRegistry.SettingRegistryEntry::getPreferencesResId)
+ // Parse
.map(parser::parse)
+ // Add it to the searcher
.forEach(searcher::add);
if (restored) {
@@ -252,7 +257,13 @@ public class SettingsActivity extends AppCompatActivity implements
searchFragment.setSearcher(searcher);
}
- private void prepareSearchConfig() {
+ /**
+ * Ensures that the search shows the correct/available search results.
+ *
+ * Some features are e.g. only available for debug builds, these should not
+ * be found when searching inside a release.
+ */
+ private void ensureSearchRepresentsApplicationState() {
// Check if the update settings are available
if (!CheckForNewAppVersion.isReleaseApk(App.getApp())) {
SettingsResourceRegistry.getInstance()
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
index afd2c1b4f..ccddee97b 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
@@ -39,27 +39,22 @@ public class PreferenceParser {
}
public List parse(
- final PreferenceSearchConfiguration.SearchIndexItem item
+ @XmlRes final int resId
) {
- Objects.requireNonNull(item, "item can't be null");
-
final List results = new ArrayList<>();
- final XmlPullParser xpp = context.getResources().getXml(item.getResId());
+ final XmlPullParser xpp = context.getResources().getXml(resId);
try {
xpp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
xpp.setFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES, true);
final List breadcrumbs = new ArrayList<>();
- if (!TextUtils.isEmpty(item.getBreadcrumb())) {
- breadcrumbs.add(item.getBreadcrumb());
- }
while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
if (xpp.getEventType() == XmlPullParser.START_TAG) {
final PreferenceSearchItem result = parseSearchResult(
xpp,
joinBreadcrumbs(breadcrumbs),
- item.getResId()
+ resId
);
if (!searchConfiguration.getParserIgnoreElements().contains(xpp.getName())
@@ -79,7 +74,7 @@ public class PreferenceParser {
xpp.next();
}
} catch (final Exception e) {
- Log.w(TAG, "Failed to parse resid=" + item.getResId(), e);
+ Log.w(TAG, "Failed to parse resid=" + resId, e);
}
return results;
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
index 1c9dc99aa..3d49140fb 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
@@ -1,14 +1,10 @@
package org.schabi.newpipe.settings.preferencesearch;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.text.TextUtils;
-import androidx.annotation.XmlRes;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -16,8 +12,6 @@ import java.util.function.BinaryOperator;
import java.util.stream.Stream;
public class PreferenceSearchConfiguration {
- private final ArrayList itemsToIndex = new ArrayList<>();
-
private BinaryOperator breadcrumbConcat =
(s1, s2) -> TextUtils.isEmpty(s1) ? s2 : (s1 + " > " + s2);
@@ -38,27 +32,11 @@ public class PreferenceSearchConfiguration {
this.searcher = Objects.requireNonNull(searcher);
}
- /**
- * Adds a new file to the index.
- *
- * @param resId The preference file to index
- * @return SearchIndexItem
- */
- public SearchIndexItem index(@XmlRes final int resId) {
- final SearchIndexItem item = new SearchIndexItem(resId, this);
- itemsToIndex.add(item);
- return item;
- }
-
- public List getFiles() {
- return itemsToIndex;
- }
-
public BinaryOperator getBreadcrumbConcat() {
return breadcrumbConcat;
}
- public PreferenceSearchFunction getSearchMatcher() {
+ public PreferenceSearchFunction getSearcher() {
return searcher;
}
@@ -70,81 +48,6 @@ public class PreferenceSearchConfiguration {
return parserContainerElements;
}
- /**
- * Adds a given R.xml resource to the search index.
- */
- public static final class SearchIndexItem implements Parcelable {
- private String breadcrumb = "";
- @XmlRes
- private final int resId;
- private final PreferenceSearchConfiguration searchConfiguration;
-
- /**
- * Includes the given R.xml resource in the index.
- *
- * @param resId The resource to index
- * @param searchConfiguration The configuration for the search
- */
- private SearchIndexItem(
- @XmlRes final int resId,
- final PreferenceSearchConfiguration searchConfiguration
- ) {
- this.resId = resId;
- this.searchConfiguration = searchConfiguration;
- }
-
- /**
- * Adds a breadcrumb.
- *
- * @param breadcrumb The breadcrumb to add
- * @return For chaining
- */
- @SuppressWarnings("HiddenField")
- public SearchIndexItem withBreadcrumb(final String breadcrumb) {
- this.breadcrumb =
- searchConfiguration.getBreadcrumbConcat().apply(this.breadcrumb, breadcrumb);
- return this;
- }
-
- @XmlRes
- int getResId() {
- return resId;
- }
-
- String getBreadcrumb() {
- return breadcrumb;
- }
-
- public static final Creator CREATOR = new Creator<>() {
- @Override
- public SearchIndexItem createFromParcel(final Parcel in) {
- return new SearchIndexItem(in);
- }
-
- @Override
- public SearchIndexItem[] newArray(final int size) {
- return new SearchIndexItem[size];
- }
- };
-
- private SearchIndexItem(final Parcel parcel) {
- this.breadcrumb = parcel.readString();
- this.resId = parcel.readInt();
- this.searchConfiguration = null;
- }
-
- @Override
- public void writeToParcel(final Parcel dest, final int flags) {
- dest.writeString(this.breadcrumb);
- dest.writeInt(this.resId);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
- }
-
@FunctionalInterface
public interface PreferenceSearchFunction {
Stream search(
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java
index 87dd11693..176dc5d14 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearcher.java
@@ -24,7 +24,7 @@ public class PreferenceSearcher {
return new ArrayList<>();
}
- return configuration.getSearchMatcher()
+ return configuration.getSearcher()
.search(allEntries.stream(), keyword)
.collect(Collectors.toList());
}
From 03bb2123f24a9ef874d508db94fcd779526c8c88 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sun, 9 Jan 2022 16:49:16 +0100
Subject: [PATCH 23/27] Removed breadcrumbs customization
---
.../org/schabi/newpipe/settings/SettingsActivity.java | 1 -
.../settings/preferencesearch/PreferenceParser.java | 3 ++-
.../PreferenceSearchConfiguration.java | 11 -----------
3 files changed, 2 insertions(+), 13 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 7d56863eb..3872e5172 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -171,7 +171,6 @@ public class SettingsActivity extends AppCompatActivity implements
@Override
public boolean onPreferenceStartFragment(final PreferenceFragmentCompat caller,
final Preference preference) {
- preference.getExtras()
showSettingsFragment(instantiateFragment(preference.getFragment()));
return true;
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
index ccddee97b..77136e08d 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
@@ -15,6 +15,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Parses the corresponding preference-file(s).
@@ -82,7 +83,7 @@ public class PreferenceParser {
private String joinBreadcrumbs(final List breadcrumbs) {
return breadcrumbs.stream()
.filter(crumb -> !TextUtils.isEmpty(crumb))
- .reduce("", searchConfiguration.getBreadcrumbConcat());
+ .collect(Collectors.joining(" > "));
}
private String getAttribute(
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
index 3d49140fb..50371b78b 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
@@ -12,9 +12,6 @@ import java.util.function.BinaryOperator;
import java.util.stream.Stream;
public class PreferenceSearchConfiguration {
- private BinaryOperator breadcrumbConcat =
- (s1, s2) -> TextUtils.isEmpty(s1) ? s2 : (s1 + " > " + s2);
-
private PreferenceSearchFunction searcher = new PreferenceFuzzySearchFunction();
private final List parserIgnoreElements = Arrays.asList(
@@ -24,18 +21,10 @@ public class PreferenceSearchConfiguration {
PreferenceScreen.class.getSimpleName());
- public void setBreadcrumbConcat(final BinaryOperator breadcrumbConcat) {
- this.breadcrumbConcat = Objects.requireNonNull(breadcrumbConcat);
- }
-
public void setSearcher(final PreferenceSearchFunction searcher) {
this.searcher = Objects.requireNonNull(searcher);
}
- public BinaryOperator getBreadcrumbConcat() {
- return breadcrumbConcat;
- }
-
public PreferenceSearchFunction getSearcher() {
return searcher;
}
From 7067ebdd128f525e039b3bd3942a4bdcf73c1980 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sun, 9 Jan 2022 16:53:36 +0100
Subject: [PATCH 24/27] Fixed imports
---
.../newpipe/settings/preferencesearch/PreferenceParser.java | 4 +++-
.../preferencesearch/PreferenceSearchConfiguration.java | 3 ---
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
index 77136e08d..1f507c7f1 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java
@@ -3,6 +3,7 @@ package org.schabi.newpipe.settings.preferencesearch;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.XmlRes;
@@ -14,7 +15,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.stream.Collectors;
/**
@@ -64,6 +64,8 @@ public class PreferenceParser {
results.add(result);
}
if (searchConfiguration.getParserContainerElements().contains(xpp.getName())) {
+ // This code adds breadcrumbs for certain containers (e.g. PreferenceScreen)
+ // Example: Video and Audio > Player
breadcrumbs.add(result.getTitle() == null ? "" : result.getTitle());
}
} else if (xpp.getEventType() == XmlPullParser.END_TAG
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
index 50371b78b..5835dcab5 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java
@@ -1,14 +1,11 @@
package org.schabi.newpipe.settings.preferencesearch;
-import android.text.TextUtils;
-
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
-import java.util.function.BinaryOperator;
import java.util.stream.Stream;
public class PreferenceSearchConfiguration {
From f55e8ea3aad21b2897e7bcecb84bb3d077d514b3 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sun, 9 Jan 2022 17:40:16 +0100
Subject: [PATCH 25/27] Use ViewBinding
---
.../PreferenceSearchFragment.java | 31 ++++++-------------
.../settings_preferencesearch_fragment.xml | 2 +-
2 files changed, 10 insertions(+), 23 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
index 38c87ea76..f0944876e 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
@@ -10,13 +10,11 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import org.schabi.newpipe.R;
+import org.schabi.newpipe.databinding.SettingsPreferencesearchFragmentBinding;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
/**
* Displays the search results.
@@ -26,7 +24,7 @@ public class PreferenceSearchFragment extends Fragment {
private PreferenceSearcher searcher;
- private SearchViewHolder viewHolder;
+ private SettingsPreferencesearchFragmentBinding binding;
private PreferenceSearchAdapter adapter;
public void setSearcher(final PreferenceSearcher searcher) {
@@ -40,17 +38,16 @@ public class PreferenceSearchFragment extends Fragment {
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState
) {
- final View rootView =
- inflater.inflate(R.layout.settings_preferencesearch_fragment, container, false);
+ // SettingsPreferenceSearchFragmentBinding.
+ binding = SettingsPreferencesearchFragmentBinding.inflate(inflater, container, false);
- viewHolder = new SearchViewHolder(rootView);
- viewHolder.recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ binding.searchResults.setLayoutManager(new LinearLayoutManager(getContext()));
adapter = new PreferenceSearchAdapter();
adapter.setOnItemClickListener(this::onItemClicked);
- viewHolder.recyclerView.setAdapter(adapter);
+ binding.searchResults.setAdapter(adapter);
- return rootView;
+ return binding.getRoot();
}
public void updateSearchResults(final String keyword) {
@@ -69,8 +66,8 @@ public class PreferenceSearchFragment extends Fragment {
}
private void setEmptyViewShown(final boolean shown) {
- viewHolder.emptyStateView.setVisibility(shown ? View.VISIBLE : View.GONE);
- viewHolder.recyclerView.setVisibility(shown ? View.GONE : View.VISIBLE);
+ binding.emptyStateView.setVisibility(shown ? View.VISIBLE : View.GONE);
+ binding.searchResults.setVisibility(shown ? View.GONE : View.VISIBLE);
}
public void onItemClicked(final PreferenceSearchItem item) {
@@ -81,14 +78,4 @@ public class PreferenceSearchFragment extends Fragment {
((PreferenceSearchResultListener) getActivity()).onSearchResultClicked(item);
}
-
- private static class SearchViewHolder {
- private final RecyclerView recyclerView;
- private final View emptyStateView;
-
- SearchViewHolder(final View root) {
- recyclerView = Objects.requireNonNull(root.findViewById(R.id.list));
- emptyStateView = Objects.requireNonNull(root.findViewById(R.id.empty_state_view));
- }
- }
}
diff --git a/app/src/main/res/layout/settings_preferencesearch_fragment.xml b/app/src/main/res/layout/settings_preferencesearch_fragment.xml
index b8aaa60f6..89a25b217 100644
--- a/app/src/main/res/layout/settings_preferencesearch_fragment.xml
+++ b/app/src/main/res/layout/settings_preferencesearch_fragment.xml
@@ -41,7 +41,7 @@
Date: Mon, 24 Jan 2022 21:08:06 +0100
Subject: [PATCH 26/27] Use view binding inside ``PreferenceViewHolder``
---
.../PreferenceSearchAdapter.java | 40 +++++++++----------
1 file changed, 18 insertions(+), 22 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java
index 527a4a595..02fbf9577 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java
@@ -4,12 +4,11 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
-import org.schabi.newpipe.R;
+import org.schabi.newpipe.databinding.SettingsPreferencesearchListItemResultBinding;
import java.util.ArrayList;
import java.util.List;
@@ -22,37 +21,38 @@ class PreferenceSearchAdapter
@NonNull
@Override
- public PreferenceSearchAdapter.PreferenceViewHolder onCreateViewHolder(
+ public PreferenceViewHolder onCreateViewHolder(
@NonNull final ViewGroup parent,
final int viewType
) {
return new PreferenceViewHolder(
- LayoutInflater
- .from(parent.getContext())
- .inflate(R.layout.settings_preferencesearch_list_item_result, parent, false));
+ SettingsPreferencesearchListItemResultBinding.inflate(
+ LayoutInflater.from(parent.getContext()),
+ parent,
+ false));
}
@Override
public void onBindViewHolder(
- @NonNull final PreferenceSearchAdapter.PreferenceViewHolder holder,
+ @NonNull final PreferenceViewHolder holder,
final int position
) {
final PreferenceSearchItem item = dataset.get(position);
- holder.title.setText(item.getTitle());
+ holder.binding.title.setText(item.getTitle());
if (TextUtils.isEmpty(item.getSummary())) {
- holder.summary.setVisibility(View.GONE);
+ holder.binding.summary.setVisibility(View.GONE);
} else {
- holder.summary.setVisibility(View.VISIBLE);
- holder.summary.setText(item.getSummary());
+ holder.binding.summary.setVisibility(View.VISIBLE);
+ holder.binding.summary.setText(item.getSummary());
}
if (TextUtils.isEmpty(item.getBreadcrumbs())) {
- holder.breadcrumbs.setVisibility(View.GONE);
+ holder.binding.breadcrumbs.setVisibility(View.GONE);
} else {
- holder.breadcrumbs.setVisibility(View.VISIBLE);
- holder.breadcrumbs.setText(item.getBreadcrumbs());
+ holder.binding.breadcrumbs.setVisibility(View.VISIBLE);
+ holder.binding.breadcrumbs.setText(item.getBreadcrumbs());
}
holder.itemView.setOnClickListener(v -> {
@@ -77,15 +77,11 @@ class PreferenceSearchAdapter
}
static class PreferenceViewHolder extends RecyclerView.ViewHolder {
- final TextView title;
- final TextView summary;
- final TextView breadcrumbs;
+ final SettingsPreferencesearchListItemResultBinding binding;
- PreferenceViewHolder(final View v) {
- super(v);
- title = v.findViewById(R.id.title);
- summary = v.findViewById(R.id.summary);
- breadcrumbs = v.findViewById(R.id.breadcrumbs);
+ PreferenceViewHolder(final SettingsPreferencesearchListItemResultBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
}
}
}
From 8a069b497f88c8f9f4c236e57131da98f49cffad Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 25 Jan 2022 20:47:53 +0100
Subject: [PATCH 27/27] Code cleanup
Co-authored-by: Stypox
---
.../settings/preferencesearch/PreferenceSearchFragment.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
index f0944876e..308abbc4e 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
@@ -38,7 +38,6 @@ public class PreferenceSearchFragment extends Fragment {
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState
) {
- // SettingsPreferenceSearchFragmentBinding.
binding = SettingsPreferencesearchFragmentBinding.inflate(inflater, container, false);
binding.searchResults.setLayoutManager(new LinearLayoutManager(getContext()));