am 5683f871: Adding an asynchronous search interface for apps search
* commit '5683f871722254e4e357cf3fb77cd28156278e51': Adding an asynchronous search interface for apps search
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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,44 +670,25 @@ 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<ComponentName> 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) {
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -114,13 +132,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.
|
||||
*/
|
||||
@@ -198,7 +209,7 @@ public class AlphabeticalAppsList {
|
||||
private Launcher mLauncher;
|
||||
|
||||
// The set of apps from the system not including predictions
|
||||
private List<AppInfo> mApps = new ArrayList<>();
|
||||
private final List<AppInfo> mApps = new ArrayList<>();
|
||||
// The set of filtered apps with the current filter
|
||||
private List<AppInfo> mFilteredApps = new ArrayList<>();
|
||||
// The current set of adapter items
|
||||
@@ -211,9 +222,10 @@ public class AlphabeticalAppsList {
|
||||
private List<ComponentName> mPredictedAppComponents = new ArrayList<>();
|
||||
// The set of predicted apps resolved from the component names and the current set of apps
|
||||
private List<AppInfo> mPredictedApps = new ArrayList<>();
|
||||
// The of ordered component names as a result of a search query
|
||||
private ArrayList<ComponentName> mSearchResults;
|
||||
private HashMap<CharSequence, String> 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<ComponentName> f) {
|
||||
if (mSearchResults != f) {
|
||||
mSearchResults = f;
|
||||
updateAdapterItems();
|
||||
}
|
||||
}
|
||||
@@ -428,7 +444,9 @@ public class AlphabeticalAppsList {
|
||||
for (Map.Entry<String, ArrayList<AppInfo>> 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<AppInfo> 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<AppInfo> getFiltersAppInfos() {
|
||||
if (mSearchResults == null) {
|
||||
return mApps;
|
||||
}
|
||||
|
||||
int total = mSearchResults.size();
|
||||
final HashMap<ComponentName, Integer> sortOrder = new HashMap<>(total);
|
||||
for (int i = 0; i < total; i++) {
|
||||
sortOrder.put(mSearchResults.get(i), i);
|
||||
}
|
||||
|
||||
ArrayList<AppInfo> result = new ArrayList<>();
|
||||
for (AppInfo info : mApps) {
|
||||
if (sortOrder.containsKey(info.componentName)) {
|
||||
result.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(result, new AbstractUserComparator<AppInfo>(
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -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<ComponentName> apps);
|
||||
}
|
||||
}
|
||||
@@ -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<AppInfo> mApps;
|
||||
private final Handler mResultHandler;
|
||||
|
||||
public SimpleAppSearchManagerImpl(List<AppInfo> 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<ComponentName> result = new ArrayList<ComponentName>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<T extends ItemInfo> implements Comparator<T> {
|
||||
|
||||
private HashMap<UserHandleCompat, Long> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<ItemInfo> mAppInfoComparator;
|
||||
private final AbstractUserComparator<ItemInfo> mAppInfoComparator;
|
||||
private final Comparator<String> mSectionNameComparator;
|
||||
private HashMap<UserHandleCompat, Long> mUserSerialCache = new HashMap<>();
|
||||
|
||||
public AppNameComparator(Context context) {
|
||||
mCollator = Collator.getInstance();
|
||||
mUserManager = UserManagerCompat.getInstance(context);
|
||||
mAppInfoComparator = new Comparator<ItemInfo>() {
|
||||
mAppInfoComparator = new AbstractUserComparator<ItemInfo>(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<ItemInfo> 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user