From 5d2c72afcd2d2345f4a2110d699a283ef05db258 Mon Sep 17 00:00:00 2001 From: Suphon Thanakornpakapong Date: Sun, 6 Jun 2021 01:05:57 +0700 Subject: [PATCH] Make fuzzy search optional --- lawnchair/res/values/strings.xml | 2 ++ .../allapps/LawnchairAppSearchAlgorithm.kt | 28 ++++++++++++++++ .../nexuslauncher/AllAppsHotseatQsb.kt | 3 +- .../ui/preferences/AppDrawerPreferences.kt | 10 +++--- .../util/preferences/PreferenceManager.kt | 1 + .../search/DefaultAppSearchAlgorithm.java | 33 ++++++++++--------- 6 files changed, 57 insertions(+), 20 deletions(-) create mode 100644 lawnchair/src/app/lawnchair/allapps/LawnchairAppSearchAlgorithm.kt diff --git a/lawnchair/res/values/strings.xml b/lawnchair/res/values/strings.xml index 967542d9ad..9858dcf0a6 100644 --- a/lawnchair/res/values/strings.xml +++ b/lawnchair/res/values/strings.xml @@ -89,4 +89,6 @@ Open Settings Show Feed Feed is not available + Fuzzy Search + Use fuzzy matching to search for apps diff --git a/lawnchair/src/app/lawnchair/allapps/LawnchairAppSearchAlgorithm.kt b/lawnchair/src/app/lawnchair/allapps/LawnchairAppSearchAlgorithm.kt new file mode 100644 index 0000000000..49cc85e5cb --- /dev/null +++ b/lawnchair/src/app/lawnchair/allapps/LawnchairAppSearchAlgorithm.kt @@ -0,0 +1,28 @@ +package app.lawnchair.allapps + +import android.content.Context +import app.lawnchair.util.preferences.PreferenceManager +import com.android.launcher3.allapps.search.DefaultAppSearchAlgorithm +import com.android.launcher3.model.data.AppInfo +import com.android.launcher3.util.ComponentKey +import me.xdrop.fuzzywuzzy.FuzzySearch +import me.xdrop.fuzzywuzzy.algorithms.WeightedRatio +import java.util.* + +class LawnchairAppSearchAlgorithm(context: Context, apps: MutableList) : DefaultAppSearchAlgorithm(apps) { + + private val useFuzzySearch by PreferenceManager.getInstance(context).useFuzzySearch + + override fun getTitleMatchResult(query: String): ArrayList { + if (!useFuzzySearch) return super.getTitleMatchResult(query) + + // Run a fuzzy search on all available titles using the Winkler-Jaro algorithm + val result = ArrayList() + val matches = FuzzySearch.extractSorted(query.toLowerCase(Locale.getDefault()), mApps, + { it.title.toString() }, WeightedRatio(), 65) + for (match in matches) { + result.add(match.referent.toComponentKey()) + } + return result + } +} diff --git a/lawnchair/src/app/lawnchair/nexuslauncher/AllAppsHotseatQsb.kt b/lawnchair/src/app/lawnchair/nexuslauncher/AllAppsHotseatQsb.kt index 6b0729d82d..f27dddb477 100644 --- a/lawnchair/src/app/lawnchair/nexuslauncher/AllAppsHotseatQsb.kt +++ b/lawnchair/src/app/lawnchair/nexuslauncher/AllAppsHotseatQsb.kt @@ -15,6 +15,7 @@ import android.view.animation.Interpolator import android.widget.EditText import android.widget.FrameLayout import androidx.recyclerview.widget.RecyclerView +import app.lawnchair.allapps.LawnchairAppSearchAlgorithm import app.lawnchair.util.preferences.PreferenceManager import com.android.launcher3.* import com.android.launcher3.allapps.AllAppsContainerView @@ -92,7 +93,7 @@ class AllAppsHotseatQsb @JvmOverloads constructor(context: Context, attrs: Attri mFallbackSearchView.elevation = initialElevation + elevationScale * initialElevation } }) - mSearchBarController.initialize(DefaultAppSearchAlgorithm(mApps.apps), + mSearchBarController.initialize(LawnchairAppSearchAlgorithm(context, mApps.apps), mFallbackSearchView, Launcher.cast(mActivity), this) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/AppDrawerPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/AppDrawerPreferences.kt index 490d8e6be3..6ab3f3bbaf 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/AppDrawerPreferences.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/AppDrawerPreferences.kt @@ -21,10 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.navigation.NavGraphBuilder -import app.lawnchair.ui.preferences.components.NavigationActionPreference -import app.lawnchair.ui.preferences.components.PreferenceGroup -import app.lawnchair.ui.preferences.components.PreferenceLayout -import app.lawnchair.ui.preferences.components.SliderPreference +import app.lawnchair.ui.preferences.components.* import app.lawnchair.util.Meta import app.lawnchair.util.pageMeta import app.lawnchair.util.preferences.getAdapter @@ -55,6 +52,11 @@ fun AppDrawerPreferences() { subtitle = resources.getQuantityString(R.plurals.apps_count, hiddenAppsCount(), hiddenAppsCount()), destination = subRoute(name = AppDrawerRoutes.HIDDEN_APPS), ) + SwitchPreference( + adapter = prefs.useFuzzySearch.getAdapter(), + label = stringResource(id = R.string.fuzzy_search_title), + description = stringResource(id = R.string.fuzzy_search_desc) + ) SliderPreference( label = stringResource(id = R.string.background_opacity), adapter = prefs.drawerOpacity.getAdapter(), diff --git a/lawnchair/src/app/lawnchair/util/preferences/PreferenceManager.kt b/lawnchair/src/app/lawnchair/util/preferences/PreferenceManager.kt index 5a689c9f3e..b6461954e1 100644 --- a/lawnchair/src/app/lawnchair/util/preferences/PreferenceManager.kt +++ b/lawnchair/src/app/lawnchair/util/preferences/PreferenceManager.kt @@ -56,6 +56,7 @@ class PreferenceManager private constructor(context: Context) : BasePreferenceMa val allAppsColumns = IdpIntPref("pref_allAppsColumns", { numAllAppsColumns }, reloadGrid) val smartSpaceEnable = BoolPref("pref_smartSpaceEnable", true, scheduleRestart) val minusOneEnable = BoolPref("pref_enableMinusOne", true) + val useFuzzySearch = BoolPref("pref_useFuzzySearch", false) // TODO: Add the ability to manually delete empty pages. val allowEmptyPages = BoolPref("pref_allowEmptyPages", false) diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java index 8e8693a8ec..31ddffb5b9 100644 --- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java @@ -12,8 +12,6 @@ * 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. - * - * Modifications copyright 2021, Lawnchair */ package com.android.launcher3.allapps.search; @@ -26,16 +24,12 @@ import java.text.Collator; import java.util.ArrayList; import java.util.List; -import me.xdrop.fuzzywuzzy.FuzzySearch; -import me.xdrop.fuzzywuzzy.algorithms.WeightedRatio; -import me.xdrop.fuzzywuzzy.model.BoundExtractedResult; - /** * The default search implementation. */ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { - private final List mApps; + protected final List mApps; protected final Handler mResultHandler; public DefaultAppSearchAlgorithm(List apps) { @@ -52,18 +46,27 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { @Override public void doSearch(final String query, - final AllAppsSearchBarController.Callbacks callback) { + final AllAppsSearchBarController.Callbacks callback) { final ArrayList result = getTitleMatchResult(query); - mResultHandler.post(() -> callback.onSearchResult(query, result)); + mResultHandler.post(new Runnable() { + + @Override + public void run() { + callback.onSearchResult(query, result); + } + }); } - private ArrayList getTitleMatchResult(String query) { - // Run a fuzzy search on all available titles using the Winkler-Jaro algorithm + protected ArrayList getTitleMatchResult(String query) { + // Do an intersection of the words in the query and each title, and filter out all the + // apps that don't match all of the words in the query. + final String queryTextLower = query.toLowerCase(); final ArrayList result = new ArrayList<>(); - final List> matches = FuzzySearch.extractSorted(query.toLowerCase(), mApps, - item -> item.title.toString(), new WeightedRatio(), 65); - for (BoundExtractedResult match : matches) { - result.add(match.getReferent().toComponentKey()); + StringMatcher matcher = StringMatcher.getInstance(); + for (AppInfo info : mApps) { + if (matches(info, queryTextLower, matcher)) { + result.add(info.toComponentKey()); + } } return result; }