From 5683f871722254e4e357cf3fb77cd28156278e51 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 29 May 2015 14:54:40 -0700 Subject: [PATCH] Adding an asynchronous search interface for apps search Change-Id: Ib09df0a3d587dc60ed888ddbd0edf058e4a1cc3e --- src/com/android/launcher3/Launcher.java | 11 +++ .../launcher3/allapps/AllAppsContainerView.java | 68 +++++++-------- .../launcher3/allapps/AlphabeticalAppsList.java | 95 ++++++++++++++++----- .../launcher3/allapps/AppSearchManager.java | 59 +++++++++++++ .../allapps/SimpleAppSearchManagerImpl.java | 98 ++++++++++++++++++++++ .../launcher3/model/AbstractUserComparator.java | 67 +++++++++++++++ .../android/launcher3/model/AppNameComparator.java | 49 +++++------ 7 files changed, 357 insertions(+), 90 deletions(-) create mode 100644 src/com/android/launcher3/allapps/AppSearchManager.java create mode 100644 src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java create mode 100644 src/com/android/launcher3/model/AbstractUserComparator.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 51f091613..ef34660df 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -100,6 +100,7 @@ import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.allapps.AppSearchManager; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; @@ -583,6 +584,11 @@ public class Launcher extends Activity } } } + + @Override + public void setSearchManager(AppSearchManager manager) { + mAppsView.setSearchManager(manager); + } }); mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() { private boolean mImportanceStored = false; @@ -1158,6 +1164,11 @@ public class Launcher extends Activity * Called to dismiss all apps if it is showing. */ public void dismissAllApps(); + + /** + * Sets the search manager to be used for app search. + */ + public void setSearchManager(AppSearchManager manager); } public interface LauncherSearchCallbacks { diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index d81f97f24..c05f7c0b9 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -41,6 +41,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import android.widget.TextView; + import com.android.launcher3.AppInfo; import com.android.launcher3.BaseContainerView; import com.android.launcher3.BubbleTextView; @@ -54,15 +55,15 @@ import com.android.launcher3.Folder; import com.android.launcher3.Insettable; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherTransitionable; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.allapps.AppSearchManager.AppSearchResultCallback; import com.android.launcher3.util.Thunk; +import java.util.ArrayList; import java.util.List; -import java.util.regex.Pattern; /** @@ -171,7 +172,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, AlphabeticalAppsList.AdapterChangedCallback, AllAppsGridAdapter.PredictionBarSpacerCallbacks, View.OnTouchListener, View.OnClickListener, View.OnLongClickListener, - ViewTreeObserver.OnPreDrawListener { + ViewTreeObserver.OnPreDrawListener, AppSearchResultCallback { public static final boolean GRID_MERGE_SECTIONS = true; @@ -183,8 +184,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private static final int FADE_OUT_DURATION = 100; private static final int SEARCH_TRANSLATION_X_DP = 18; - private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); - @Thunk Launcher mLauncher; @Thunk AlphabeticalAppsList mApps; private LayoutInflater mLayoutInflater; @@ -221,6 +220,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private CheckLongPressHelper mPredictionIconCheckForLongPress; private View mPredictionIconUnderTouch; + private AppSearchManager mSearchManager; + public AllAppsContainerView(Context context) { this(context, null); } @@ -231,7 +232,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - LauncherAppState app = LauncherAppState.getInstance(); Resources res = context.getResources(); mLauncher = (Launcher) context; @@ -258,6 +258,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mContentMarginStart = mAdapter.getContentMarginStart(); mApps.setAdapter(mAdapter); + mSearchManager = mApps.newSimpleAppSearchManager(); } /** @@ -281,6 +282,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mApps.addApps(apps); } + public void setSearchManager(AppSearchManager searchManager) { + mSearchManager.cancel(true); + mSearchManager = searchManager; + } + /** * Updates existing apps in the list */ @@ -664,45 +670,26 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc public void afterTextChanged(final Editable s) { String queryText = s.toString(); if (queryText.isEmpty()) { - mApps.setFilter(null); + mSearchManager.cancel(true); + mApps.setOrderedFilter(null); } else { String formatStr = getResources().getString(R.string.all_apps_no_search_results); mAdapter.setEmptySearchText(String.format(formatStr, queryText)); - // 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 = queryText.toLowerCase(); - final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); - mApps.setFilter(new AlphabeticalAppsList.Filter() { - @Override - public boolean retainApp(AppInfo info, String sectionName) { - if (sectionName.toLowerCase().contains(queryTextLower)) { - return true; - } - String title = info.title.toString(); - String[] words = SPLIT_PATTERN.split(title.toLowerCase()); - for (int qi = 0; qi < queryWords.length; qi++) { - boolean foundMatch = false; - for (int i = 0; i < words.length; i++) { - if (words[i].startsWith(queryWords[qi])) { - foundMatch = true; - break; - } - } - if (!foundMatch) { - // If there is a word in the query that does not match any words in this - // title, so skip it. - return false; - } - } - return true; - } - }); + mSearchManager.cancel(false); + mSearchManager.doSearch(queryText, this); } scrollToTop(); } @Override + public void onSearchResult(ArrayList apps) { + if (apps != null) { + mApps.setOrderedFilter(apps); + } + } + + @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (ALLOW_SINGLE_APP_LAUNCH && actionId == EditorInfo.IME_ACTION_DONE) { // Skip the quick-launch if there isn't exactly one item @@ -796,7 +783,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc * recycler view. */ private boolean handleTouchEvent(MotionEvent ev) { - LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = mLauncher.getDeviceProfile(); int x = (int) ev.getX(); int y = (int) ev.getY(); @@ -919,6 +905,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc * Shows the search field. */ private void showSearchField() { + mSearchManager.connect(); + // Show the search bar and focus the search final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, getContext().getResources().getDisplayMetrics()); @@ -949,6 +937,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc * Hides the search field. */ private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { + mSearchManager.cancel(true); + final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, getContext().getResources().getDisplayMetrics()); @@ -966,7 +956,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc if (resetTextField) { mSearchBarEditView.setText(""); } - mApps.setFilter(null); + mApps.setOrderedFilter(null); if (returnFocusToRecyclerView) { mAppsRecyclerView.requestFocus(); } @@ -983,7 +973,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc if (resetTextField) { mSearchBarEditView.setText(""); } - mApps.setFilter(null); + mApps.setOrderedFilter(null); mSearchButtonView.setAlpha(1f); mSearchButtonView.setTranslationX(0f); if (returnFocusToRecyclerView) { diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 13e18289a..0dc2d1e63 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -1,13 +1,31 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed 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 com.android.launcher3.allapps; import android.content.ComponentName; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.Log; + import com.android.launcher3.AppInfo; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.compat.AlphabeticIndexCompat; +import com.android.launcher3.model.AbstractUserComparator; import com.android.launcher3.model.AppNameComparator; import java.nio.charset.CharsetEncoder; @@ -115,13 +133,6 @@ public class AlphabeticalAppsList { } /** - * A filter interface to limit the set of applications in the apps list. - */ - public interface Filter { - boolean retainApp(AppInfo info, String sectionName); - } - - /** * Callback to notify when the set of adapter items have changed. */ public interface AdapterChangedCallback { @@ -198,7 +209,7 @@ public class AlphabeticalAppsList { private Launcher mLauncher; // The set of apps from the system not including predictions - private List mApps = new ArrayList<>(); + private final List mApps = new ArrayList<>(); // The set of filtered apps with the current filter private List mFilteredApps = new ArrayList<>(); // The current set of adapter items @@ -211,9 +222,10 @@ public class AlphabeticalAppsList { private List mPredictedAppComponents = new ArrayList<>(); // The set of predicted apps resolved from the component names and the current set of apps private List mPredictedApps = new ArrayList<>(); + // The of ordered component names as a result of a search query + private ArrayList mSearchResults; private HashMap mCachedSectionNames = new HashMap<>(); private RecyclerView.Adapter mAdapter; - private Filter mFilter; private AlphabeticIndexCompat mIndexer; private AppNameComparator mAppNameComparator; private MergeAlgorithm mMergeAlgorithm; @@ -235,6 +247,10 @@ public class AlphabeticalAppsList { mAdapterChangedCallback = cb; } + public SimpleAppSearchManagerImpl newSimpleAppSearchManager() { + return new SimpleAppSearchManagerImpl(mApps); + } + /** * Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED. */ @@ -293,22 +309,22 @@ public class AlphabeticalAppsList { * Returns whether there are is a filter set. */ public boolean hasFilter() { - return (mFilter != null); + return (mSearchResults != null); } /** * Returns whether there are no filtered results. */ public boolean hasNoFilteredResults() { - return (mFilter != null) && mFilteredApps.isEmpty(); + return (mSearchResults != null) && mFilteredApps.isEmpty(); } /** - * Sets the current filter for this list of apps. + * Sets the sorted list of filtered components. */ - public void setFilter(Filter f) { - if (mFilter != f) { - mFilter = f; + public void setOrderedFilter(ArrayList f) { + if (mSearchResults != f) { + mSearchResults = f; updateAdapterItems(); } } @@ -428,7 +444,9 @@ public class AlphabeticalAppsList { for (Map.Entry> entry : sectionMap.entrySet()) { allApps.addAll(entry.getValue()); } - mApps = allApps; + + mApps.clear(); + mApps.addAll(allApps); } else { // Just compute the section headers for use below for (AppInfo info : mApps) { @@ -483,16 +501,12 @@ public class AlphabeticalAppsList { // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the // ordered set of sections - int numApps = mApps.size(); + List apps = getFiltersAppInfos(); + int numApps = apps.size(); for (int i = 0; i < numApps; i++) { - AppInfo info = mApps.get(i); + AppInfo info = apps.get(i); String sectionName = getAndUpdateCachedSectionName(info.title); - // Check if we want to retain this app - if (mFilter != null && !mFilter.retainApp(info, sectionName)) { - continue; - } - // Create a new section if the section names do not match if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) { lastSectionName = sectionName; @@ -533,6 +547,41 @@ public class AlphabeticalAppsList { } } + private List getFiltersAppInfos() { + if (mSearchResults == null) { + return mApps; + } + + int total = mSearchResults.size(); + final HashMap sortOrder = new HashMap<>(total); + for (int i = 0; i < total; i++) { + sortOrder.put(mSearchResults.get(i), i); + } + + ArrayList result = new ArrayList<>(); + for (AppInfo info : mApps) { + if (sortOrder.containsKey(info.componentName)) { + result.add(info); + } + } + + Collections.sort(result, new AbstractUserComparator( + LauncherAppState.getInstance().getContext()) { + + @Override + public int compare(AppInfo lhs, AppInfo rhs) { + Integer indexA = sortOrder.get(lhs.componentName); + int result = indexA.compareTo(sortOrder.get(rhs.componentName)); + if (result == 0) { + return super.compare(lhs, rhs); + } else { + return result; + } + } + }); + return result; + } + /** * Merges multiple sections to reduce visual raggedness. */ diff --git a/src/com/android/launcher3/allapps/AppSearchManager.java b/src/com/android/launcher3/allapps/AppSearchManager.java new file mode 100644 index 000000000..b6aa22341 --- /dev/null +++ b/src/com/android/launcher3/allapps/AppSearchManager.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed 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 com.android.launcher3.allapps; + +import android.content.ComponentName; + +import java.util.ArrayList; + +/** + * Interface for handling app search. + */ +public interface AppSearchManager { + + /** + * Called when the search is about to be used. This method is optional for making a query but + * calling this appropriately can improve the initial response time. + */ + void connect(); + + /** + * Cancels all pending search requests. + * + * @param interruptActiveRequests if true, any active requests which are already executing will + * be invalidated, and the corresponding results will not be sent. The client should usually + * set this to true, before beginning a new search session. + */ + void cancel(boolean interruptActiveRequests); + + /** + * Performs a search + */ + void doSearch(String query, AppSearchResultCallback callback); + + /** + * Callback for getting search results. + */ + public interface AppSearchResultCallback { + + /** + * Called when the search is complete. + * + * @param apps sorted list of matching components or null if in case of failure. + */ + void onSearchResult(ArrayList apps); + } +} diff --git a/src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java b/src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java new file mode 100644 index 000000000..e8a31b546 --- /dev/null +++ b/src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed 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 com.android.launcher3.allapps; + +import android.content.ComponentName; +import android.os.Handler; + +import com.android.launcher3.AppInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * An {@link AppSearchManager} which does label matching on the UI thread. + */ +public class SimpleAppSearchManagerImpl implements AppSearchManager { + + private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); + + private final List mApps; + private final Handler mResultHandler; + + public SimpleAppSearchManagerImpl(List apps) { + mApps = apps; + mResultHandler = new Handler(); + } + + @Override + public void connect() { + // No op + } + + @Override + public void cancel(boolean interruptActiveRequests) { + if (interruptActiveRequests) { + mResultHandler.removeCallbacksAndMessages(null); + } + } + + @Override + public void doSearch(String query, final AppSearchResultCallback callback) { + // 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 String[] queryWords = SPLIT_PATTERN.split(queryTextLower); + final ArrayList result = new ArrayList(); + int total = mApps.size(); + + for (int i = 0; i < total; i++) { + AppInfo info = mApps.get(i); + if (!result.contains(info.componentName) && matches(info, queryWords)) { + result.add(info.componentName); + } + } + mResultHandler.post(new Runnable() { + + @Override + public void run() { + callback.onSearchResult(result); + } + }); + } + + private boolean matches(AppInfo info, String[] queryWords) { + String title = info.title.toString(); + String[] words = SPLIT_PATTERN.split(title.toLowerCase()); + for (int qi = 0; qi < queryWords.length; qi++) { + boolean foundMatch = false; + for (int i = 0; i < words.length; i++) { + if (words[i].startsWith(queryWords[qi])) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + // If there is a word in the query that does not match any words in this + // title, so skip it. + return false; + } + } + return true; + } + +} diff --git a/src/com/android/launcher3/model/AbstractUserComparator.java b/src/com/android/launcher3/model/AbstractUserComparator.java new file mode 100644 index 000000000..cf47ce648 --- /dev/null +++ b/src/com/android/launcher3/model/AbstractUserComparator.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed 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 com.android.launcher3.model; + +import android.content.Context; + +import com.android.launcher3.ItemInfo; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; + +import java.util.Comparator; +import java.util.HashMap; + +/** + * A comparator to arrange items based on user profiles. + */ +public abstract class AbstractUserComparator implements Comparator { + + private HashMap mUserSerialCache = new HashMap<>(); + private final UserManagerCompat mUserManager; + private final UserHandleCompat mMyUser; + + public AbstractUserComparator(Context context) { + mUserManager = UserManagerCompat.getInstance(context); + mMyUser = UserHandleCompat.myUserHandle(); + } + + @Override + public int compare(T lhs, T rhs) { + if (mMyUser.equals(lhs.user)) { + return -1; + } else { + Long aUserSerial = getAndCacheUserSerial(lhs.user); + Long bUserSerial = getAndCacheUserSerial(rhs.user); + return aUserSerial.compareTo(bUserSerial); + } + } + + /** + * Returns the user serial for this user, using a cached serial if possible. + */ + private Long getAndCacheUserSerial(UserHandleCompat user) { + Long userSerial = mUserSerialCache.get(user); + if (userSerial == null) { + userSerial = mUserManager.getSerialNumberForUser(user); + mUserSerialCache.put(user, userSerial); + } + return userSerial; + } + + public void clearUserCache() { + mUserSerialCache.clear(); + } +} diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java index 706f7515d..cdac40ac0 100644 --- a/src/com/android/launcher3/model/AppNameComparator.java +++ b/src/com/android/launcher3/model/AppNameComparator.java @@ -1,14 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed 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 com.android.launcher3.model; import android.content.Context; import com.android.launcher3.AppInfo; import com.android.launcher3.ItemInfo; -import com.android.launcher3.compat.UserHandleCompat; -import com.android.launcher3.compat.UserManagerCompat; + import java.text.Collator; import java.util.Comparator; -import java.util.HashMap; /** * Class to manage access to an app name comparator. @@ -16,17 +29,15 @@ import java.util.HashMap; * Used to sort application name in all apps view and widget tray view. */ public class AppNameComparator { - private final UserManagerCompat mUserManager; private final Collator mCollator; - private final Comparator mAppInfoComparator; + private final AbstractUserComparator mAppInfoComparator; private final Comparator mSectionNameComparator; - private HashMap mUserSerialCache = new HashMap<>(); public AppNameComparator(Context context) { mCollator = Collator.getInstance(); - mUserManager = UserManagerCompat.getInstance(context); - mAppInfoComparator = new Comparator() { + mAppInfoComparator = new AbstractUserComparator(context) { + @Override public final int compare(ItemInfo a, ItemInfo b) { // Order by the title in the current locale int result = compareTitles(a.title.toString(), b.title.toString()); @@ -38,13 +49,7 @@ public class AppNameComparator { if (result == 0) { // If the two apps are the same component, then prioritize by the order that // the app user was created (prioritizing the main user's apps) - if (UserHandleCompat.myUserHandle().equals(a.user)) { - return -1; - } else { - Long aUserSerial = getAndCacheUserSerial(a.user); - Long bUserSerial = getAndCacheUserSerial(b.user); - return aUserSerial.compareTo(bUserSerial); - } + return super.compare(a, b); } } return result; @@ -63,7 +68,7 @@ public class AppNameComparator { */ public Comparator getAppInfoComparator() { // Clear the user serial cache so that we get serials as needed in the comparator - mUserSerialCache.clear(); + mAppInfoComparator.clearUserCache(); return mAppInfoComparator; } @@ -90,16 +95,4 @@ public class AppNameComparator { // Order by the title in the current locale return mCollator.compare(titleA, titleB); } - - /** - * Returns the user serial for this user, using a cached serial if possible. - */ - private Long getAndCacheUserSerial(UserHandleCompat user) { - Long userSerial = mUserSerialCache.get(user); - if (userSerial == null) { - userSerial = mUserManager.getSerialNumberForUser(user); - mUserSerialCache.put(user, userSerial); - } - return userSerial; - } } -- 2.11.0