8ff9e1d679
Guarded via feature flag SUGGESTED_APP. Bug: 62483883 Change-Id: I7e0c8a9875bc37c435aa4316b44322a628cd4826
666 lines
25 KiB
Java
666 lines
25 KiB
Java
/*
|
|
* 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.Context;
|
|
import android.os.Process;
|
|
import android.support.annotation.NonNull;
|
|
import android.support.annotation.Nullable;
|
|
import android.util.Log;
|
|
|
|
import com.android.launcher3.AppInfo;
|
|
import com.android.launcher3.Launcher;
|
|
import com.android.launcher3.compat.AlphabeticIndexCompat;
|
|
import com.android.launcher3.config.FeatureFlags;
|
|
import com.android.launcher3.discovery.AppDiscoveryAppInfo;
|
|
import com.android.launcher3.discovery.AppDiscoveryItem;
|
|
import com.android.launcher3.discovery.AppDiscoveryUpdateState;
|
|
import com.android.launcher3.util.ComponentKey;
|
|
import com.android.launcher3.util.ComponentKeyMapper;
|
|
import com.android.launcher3.util.LabelComparator;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.TreeMap;
|
|
|
|
/**
|
|
* The alphabetically sorted list of applications.
|
|
*/
|
|
public class AlphabeticalAppsList {
|
|
|
|
public static final String TAG = "AlphabeticalAppsList";
|
|
private static final boolean DEBUG = false;
|
|
private static final boolean DEBUG_PREDICTIONS = false;
|
|
|
|
private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION = 0;
|
|
private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1;
|
|
|
|
private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS;
|
|
|
|
private AppDiscoveryUpdateState mAppDiscoveryUpdateState;
|
|
|
|
/**
|
|
* Info about a fast scroller section, depending if sections are merged, the fast scroller
|
|
* sections will not be the same set as the section headers.
|
|
*/
|
|
public static class FastScrollSectionInfo {
|
|
// The section name
|
|
public String sectionName;
|
|
// The AdapterItem to scroll to for this section
|
|
public AdapterItem fastScrollToItem;
|
|
// The touch fraction that should map to this fast scroll section info
|
|
public float touchFraction;
|
|
|
|
public FastScrollSectionInfo(String sectionName) {
|
|
this.sectionName = sectionName;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Info about a particular adapter item (can be either section or app)
|
|
*/
|
|
public static class AdapterItem {
|
|
/** Common properties */
|
|
// The index of this adapter item in the list
|
|
public int position;
|
|
// The type of this item
|
|
public int viewType;
|
|
|
|
/** App-only properties */
|
|
// The section name of this app. Note that there can be multiple items with different
|
|
// sectionNames in the same section
|
|
public String sectionName = null;
|
|
// The row that this item shows up on
|
|
public int rowIndex;
|
|
// The index of this app in the row
|
|
public int rowAppIndex;
|
|
// The associated AppInfo for the app
|
|
public AppInfo appInfo = null;
|
|
// The index of this app not including sections
|
|
public int appIndex = -1;
|
|
|
|
public static AdapterItem asPredictedApp(int pos, String sectionName, AppInfo appInfo,
|
|
int appIndex) {
|
|
AdapterItem item = asApp(pos, sectionName, appInfo, appIndex);
|
|
item.viewType = AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON;
|
|
return item;
|
|
}
|
|
|
|
public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
|
|
int appIndex) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = AllAppsGridAdapter.VIEW_TYPE_ICON;
|
|
item.position = pos;
|
|
item.sectionName = sectionName;
|
|
item.appInfo = appInfo;
|
|
item.appIndex = appIndex;
|
|
return item;
|
|
}
|
|
|
|
public static AdapterItem asDiscoveryItem(int pos, String sectionName, AppInfo appInfo,
|
|
int appIndex) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = AllAppsGridAdapter.VIEW_TYPE_DISCOVERY_ITEM;
|
|
item.position = pos;
|
|
item.sectionName = sectionName;
|
|
item.appInfo = appInfo;
|
|
item.appIndex = appIndex;
|
|
return item;
|
|
}
|
|
|
|
public static AdapterItem asEmptySearch(int pos) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH;
|
|
item.position = pos;
|
|
return item;
|
|
}
|
|
|
|
public static AdapterItem asPredictionDivider(int pos) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER;
|
|
item.position = pos;
|
|
return item;
|
|
}
|
|
|
|
public static AdapterItem asMarketDivider(int pos) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER;
|
|
item.position = pos;
|
|
return item;
|
|
}
|
|
|
|
public static AdapterItem asLoadingDivider(int pos) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = AllAppsGridAdapter.VIEW_TYPE_APPS_LOADING_DIVIDER;
|
|
item.position = pos;
|
|
return item;
|
|
}
|
|
|
|
public static AdapterItem asMarketSearch(int pos) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET;
|
|
item.position = pos;
|
|
return item;
|
|
}
|
|
}
|
|
|
|
private final Launcher mLauncher;
|
|
|
|
// The set of apps from the system not including predictions
|
|
private final List<AppInfo> mApps = new ArrayList<>();
|
|
private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
|
|
|
|
// The set of filtered apps with the current filter
|
|
private final List<AppInfo> mFilteredApps = new ArrayList<>();
|
|
// The current set of adapter items
|
|
private final ArrayList<AdapterItem> mAdapterItems = new ArrayList<>();
|
|
// The set of sections that we allow fast-scrolling to (includes non-merged sections)
|
|
private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
|
|
// The set of predicted app component names
|
|
private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
|
|
// The set of predicted apps resolved from the component names and the current set of apps
|
|
private final List<AppInfo> mPredictedApps = new ArrayList<>();
|
|
private final List<AppDiscoveryAppInfo> mDiscoveredApps = new ArrayList<>();
|
|
|
|
// The of ordered component names as a result of a search query
|
|
private ArrayList<ComponentKey> mSearchResults;
|
|
private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
|
|
private AllAppsGridAdapter mAdapter;
|
|
private AlphabeticIndexCompat mIndexer;
|
|
private AppInfoComparator mAppNameComparator;
|
|
private int mNumAppsPerRow;
|
|
private int mNumPredictedAppsPerRow;
|
|
private int mNumAppRowsInAdapter;
|
|
|
|
public AlphabeticalAppsList(Context context) {
|
|
mLauncher = Launcher.getLauncher(context);
|
|
mIndexer = new AlphabeticIndexCompat(context);
|
|
mAppNameComparator = new AppInfoComparator(context);
|
|
}
|
|
|
|
/**
|
|
* Sets the number of apps per row.
|
|
*/
|
|
public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
|
|
mNumAppsPerRow = numAppsPerRow;
|
|
mNumPredictedAppsPerRow = numPredictedAppsPerRow;
|
|
|
|
updateAdapterItems();
|
|
}
|
|
|
|
/**
|
|
* Sets the adapter to notify when this dataset changes.
|
|
*/
|
|
public void setAdapter(AllAppsGridAdapter adapter) {
|
|
mAdapter = adapter;
|
|
}
|
|
|
|
/**
|
|
* Returns all the apps.
|
|
*/
|
|
public List<AppInfo> getApps() {
|
|
return mApps;
|
|
}
|
|
|
|
/**
|
|
* Returns the predicted apps.
|
|
*/
|
|
public List<AppInfo> getPredictedApps() {
|
|
return mPredictedApps;
|
|
}
|
|
|
|
/**
|
|
* Returns fast scroller sections of all the current filtered applications.
|
|
*/
|
|
public List<FastScrollSectionInfo> getFastScrollerSections() {
|
|
return mFastScrollerSections;
|
|
}
|
|
|
|
/**
|
|
* Returns the current filtered list of applications broken down into their sections.
|
|
*/
|
|
public List<AdapterItem> getAdapterItems() {
|
|
return mAdapterItems;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of rows of applications (not including predictions)
|
|
*/
|
|
public int getNumAppRows() {
|
|
return mNumAppRowsInAdapter;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of applications in this list.
|
|
*/
|
|
public int getNumFilteredApps() {
|
|
return mFilteredApps.size();
|
|
}
|
|
|
|
/**
|
|
* Returns whether there are is a filter set.
|
|
*/
|
|
public boolean hasFilter() {
|
|
return (mSearchResults != null);
|
|
}
|
|
|
|
/**
|
|
* Returns whether there are no filtered results.
|
|
*/
|
|
public boolean hasNoFilteredResults() {
|
|
return (mSearchResults != null) && mFilteredApps.isEmpty();
|
|
}
|
|
|
|
boolean shouldShowEmptySearch() {
|
|
return hasNoFilteredResults() && !isAppDiscoveryRunning() && mDiscoveredApps.isEmpty();
|
|
}
|
|
|
|
/**
|
|
* Sets the sorted list of filtered components.
|
|
*/
|
|
public boolean setOrderedFilter(ArrayList<ComponentKey> f) {
|
|
if (mSearchResults != f) {
|
|
boolean same = mSearchResults != null && mSearchResults.equals(f);
|
|
mSearchResults = f;
|
|
updateAdapterItems();
|
|
return !same;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void onAppDiscoverySearchUpdate(@Nullable AppDiscoveryItem app,
|
|
@NonNull AppDiscoveryUpdateState state) {
|
|
mAppDiscoveryUpdateState = state;
|
|
switch (state) {
|
|
case START:
|
|
mDiscoveredApps.clear();
|
|
break;
|
|
case UPDATE:
|
|
mDiscoveredApps.add(new AppDiscoveryAppInfo(app));
|
|
break;
|
|
}
|
|
updateAdapterItems();
|
|
}
|
|
|
|
private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) {
|
|
if (mComponentToAppMap.isEmpty()) {
|
|
// Apps have not been bound yet.
|
|
return Collections.emptyList();
|
|
}
|
|
|
|
List<AppInfo> predictedApps = new ArrayList<>();
|
|
for (ComponentKeyMapper<AppInfo> mapper : components) {
|
|
AppInfo info = mapper.getItem(mComponentToAppMap);
|
|
if (info != null) {
|
|
predictedApps.add(info);
|
|
} else {
|
|
if (FeatureFlags.IS_DOGFOOD_BUILD) {
|
|
Log.e(TAG, "Predicted app not found: " + mapper);
|
|
}
|
|
}
|
|
// Stop at the number of predicted apps
|
|
if (predictedApps.size() == mNumPredictedAppsPerRow) {
|
|
break;
|
|
}
|
|
}
|
|
return predictedApps;
|
|
}
|
|
|
|
/**
|
|
* Sets the current set of predicted apps.
|
|
*
|
|
* This can be called before we get the full set of applications, we should merge the results
|
|
* only in onAppsUpdated() which is idempotent.
|
|
*
|
|
* If the number of predicted apps is the same as the previous list of predicted apps,
|
|
* we can optimize by swapping them in place.
|
|
*/
|
|
public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
|
|
mPredictedAppComponents.clear();
|
|
mPredictedAppComponents.addAll(apps);
|
|
|
|
List<AppInfo> newPredictedApps = processPredictedAppComponents(apps);
|
|
// We only need to do work if any of the visible predicted apps have changed.
|
|
if (!newPredictedApps.equals(mPredictedApps)) {
|
|
if (newPredictedApps.size() == mPredictedApps.size()) {
|
|
swapInNewPredictedApps(newPredictedApps);
|
|
} else {
|
|
// We need to update the appIndex of all the items.
|
|
onAppsUpdated();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Swaps out the old predicted apps with the new predicted apps, in place. This optimization
|
|
* allows us to skip an entire relayout that would otherwise be called by notifyDataSetChanged.
|
|
*
|
|
* Note: This should only be called if the # of predicted apps is the same.
|
|
* This method assumes that predicted apps are the first items in the adapter.
|
|
*/
|
|
private void swapInNewPredictedApps(List<AppInfo> apps) {
|
|
mPredictedApps.clear();
|
|
mPredictedApps.addAll(apps);
|
|
|
|
int size = apps.size();
|
|
for (int i = 0; i < size; ++i) {
|
|
AppInfo info = apps.get(i);
|
|
AdapterItem appItem = AdapterItem.asPredictedApp(i, "", info, i);
|
|
appItem.rowAppIndex = i;
|
|
mAdapterItems.set(i, appItem);
|
|
mFilteredApps.set(i, info);
|
|
mAdapter.notifyItemChanged(i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the current set of apps.
|
|
*/
|
|
public void setApps(List<AppInfo> apps) {
|
|
mComponentToAppMap.clear();
|
|
addOrUpdateApps(apps);
|
|
}
|
|
|
|
/**
|
|
* Adds or updates existing apps in the list
|
|
*/
|
|
public void addOrUpdateApps(List<AppInfo> apps) {
|
|
for (AppInfo app : apps) {
|
|
mComponentToAppMap.put(app.toComponentKey(), app);
|
|
}
|
|
onAppsUpdated();
|
|
}
|
|
|
|
/**
|
|
* Removes some apps from the list.
|
|
*/
|
|
public void removeApps(List<AppInfo> apps) {
|
|
for (AppInfo app : apps) {
|
|
mComponentToAppMap.remove(app.toComponentKey());
|
|
}
|
|
onAppsUpdated();
|
|
}
|
|
|
|
/**
|
|
* Updates internals when the set of apps are updated.
|
|
*/
|
|
private void onAppsUpdated() {
|
|
// Sort the list of apps
|
|
mApps.clear();
|
|
mApps.addAll(mComponentToAppMap.values());
|
|
Collections.sort(mApps, mAppNameComparator);
|
|
|
|
// As a special case for some languages (currently only Simplified Chinese), we may need to
|
|
// coalesce sections
|
|
Locale curLocale = mLauncher.getResources().getConfiguration().locale;
|
|
boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
|
|
if (localeRequiresSectionSorting) {
|
|
// Compute the section headers. We use a TreeMap with the section name comparator to
|
|
// ensure that the sections are ordered when we iterate over it later
|
|
TreeMap<String, ArrayList<AppInfo>> sectionMap = new TreeMap<>(new LabelComparator());
|
|
for (AppInfo info : mApps) {
|
|
// Add the section to the cache
|
|
String sectionName = getAndUpdateCachedSectionName(info.title);
|
|
|
|
// Add it to the mapping
|
|
ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
|
|
if (sectionApps == null) {
|
|
sectionApps = new ArrayList<>();
|
|
sectionMap.put(sectionName, sectionApps);
|
|
}
|
|
sectionApps.add(info);
|
|
}
|
|
|
|
// Add each of the section apps to the list in order
|
|
mApps.clear();
|
|
for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
|
|
mApps.addAll(entry.getValue());
|
|
}
|
|
} else {
|
|
// Just compute the section headers for use below
|
|
for (AppInfo info : mApps) {
|
|
// Add the section to the cache
|
|
getAndUpdateCachedSectionName(info.title);
|
|
}
|
|
}
|
|
|
|
// Recompose the set of adapter items from the current set of apps
|
|
updateAdapterItems();
|
|
}
|
|
|
|
/**
|
|
* Updates the set of filtered apps with the current filter. At this point, we expect
|
|
* mCachedSectionNames to have been calculated for the set of all apps in mApps.
|
|
*/
|
|
private void updateAdapterItems() {
|
|
refillAdapterItems();
|
|
refreshRecyclerView();
|
|
}
|
|
|
|
private void refreshRecyclerView() {
|
|
if (mAdapter != null) {
|
|
mAdapter.notifyDataSetChanged();
|
|
}
|
|
}
|
|
|
|
private void refillAdapterItems() {
|
|
String lastSectionName = null;
|
|
FastScrollSectionInfo lastFastScrollerSectionInfo = null;
|
|
int position = 0;
|
|
int appIndex = 0;
|
|
|
|
// Prepare to update the list of sections, filtered apps, etc.
|
|
mFilteredApps.clear();
|
|
mFastScrollerSections.clear();
|
|
mAdapterItems.clear();
|
|
|
|
if (DEBUG_PREDICTIONS) {
|
|
if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) {
|
|
mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
|
|
Process.myUserHandle())));
|
|
mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
|
|
Process.myUserHandle())));
|
|
mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
|
|
Process.myUserHandle())));
|
|
mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
|
|
Process.myUserHandle())));
|
|
}
|
|
}
|
|
|
|
// Process the predicted app components
|
|
mPredictedApps.clear();
|
|
if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
|
|
mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
|
|
|
|
if (!mPredictedApps.isEmpty()) {
|
|
// Add a section for the predictions
|
|
lastFastScrollerSectionInfo = new FastScrollSectionInfo("");
|
|
mFastScrollerSections.add(lastFastScrollerSectionInfo);
|
|
|
|
// Add the predicted app items
|
|
for (AppInfo info : mPredictedApps) {
|
|
AdapterItem appItem = AdapterItem.asPredictedApp(position++, "", info,
|
|
appIndex++);
|
|
if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
|
|
lastFastScrollerSectionInfo.fastScrollToItem = appItem;
|
|
}
|
|
mAdapterItems.add(appItem);
|
|
mFilteredApps.add(info);
|
|
}
|
|
|
|
mAdapterItems.add(AdapterItem.asPredictionDivider(position++));
|
|
}
|
|
}
|
|
|
|
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
|
|
// ordered set of sections
|
|
for (AppInfo info : getFiltersAppInfos()) {
|
|
String sectionName = getAndUpdateCachedSectionName(info.title);
|
|
|
|
// Create a new section if the section names do not match
|
|
if (!sectionName.equals(lastSectionName)) {
|
|
lastSectionName = sectionName;
|
|
lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
|
|
mFastScrollerSections.add(lastFastScrollerSectionInfo);
|
|
}
|
|
|
|
// Create an app item
|
|
AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++);
|
|
if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
|
|
lastFastScrollerSectionInfo.fastScrollToItem = appItem;
|
|
}
|
|
mAdapterItems.add(appItem);
|
|
mFilteredApps.add(info);
|
|
}
|
|
|
|
if (hasFilter()) {
|
|
if (isAppDiscoveryRunning() || mDiscoveredApps.size() > 0) {
|
|
mAdapterItems.add(AdapterItem.asLoadingDivider(position++));
|
|
// Append all app discovery results
|
|
for (int i = 0; i < mDiscoveredApps.size(); i++) {
|
|
AppDiscoveryAppInfo appDiscoveryAppInfo = mDiscoveredApps.get(i);
|
|
if (appDiscoveryAppInfo.isRecent) {
|
|
// already handled in getFilteredAppInfos()
|
|
continue;
|
|
}
|
|
AdapterItem item = AdapterItem.asDiscoveryItem(position++,
|
|
"", appDiscoveryAppInfo, appIndex++);
|
|
mAdapterItems.add(item);
|
|
}
|
|
|
|
if (!isAppDiscoveryRunning()) {
|
|
mAdapterItems.add(AdapterItem.asMarketSearch(position++));
|
|
}
|
|
} else {
|
|
// Append the search market item
|
|
if (hasNoFilteredResults()) {
|
|
mAdapterItems.add(AdapterItem.asEmptySearch(position++));
|
|
} else {
|
|
mAdapterItems.add(AdapterItem.asMarketDivider(position++));
|
|
}
|
|
mAdapterItems.add(AdapterItem.asMarketSearch(position++));
|
|
}
|
|
}
|
|
|
|
if (mNumAppsPerRow != 0) {
|
|
// Update the number of rows in the adapter after we do all the merging (otherwise, we
|
|
// would have to shift the values again)
|
|
int numAppsInSection = 0;
|
|
int numAppsInRow = 0;
|
|
int rowIndex = -1;
|
|
for (AdapterItem item : mAdapterItems) {
|
|
item.rowIndex = 0;
|
|
if (AllAppsGridAdapter.isDividerViewType(item.viewType)) {
|
|
numAppsInSection = 0;
|
|
} else if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
|
|
if (numAppsInSection % mNumAppsPerRow == 0) {
|
|
numAppsInRow = 0;
|
|
rowIndex++;
|
|
}
|
|
item.rowIndex = rowIndex;
|
|
item.rowAppIndex = numAppsInRow;
|
|
numAppsInSection++;
|
|
numAppsInRow++;
|
|
}
|
|
}
|
|
mNumAppRowsInAdapter = rowIndex + 1;
|
|
|
|
// Pre-calculate all the fast scroller fractions
|
|
switch (mFastScrollDistributionMode) {
|
|
case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION:
|
|
float rowFraction = 1f / mNumAppRowsInAdapter;
|
|
for (FastScrollSectionInfo info : mFastScrollerSections) {
|
|
AdapterItem item = info.fastScrollToItem;
|
|
if (!AllAppsGridAdapter.isIconViewType(item.viewType)) {
|
|
info.touchFraction = 0f;
|
|
continue;
|
|
}
|
|
|
|
float subRowFraction = item.rowAppIndex * (rowFraction / mNumAppsPerRow);
|
|
info.touchFraction = item.rowIndex * rowFraction + subRowFraction;
|
|
}
|
|
break;
|
|
case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS:
|
|
float perSectionTouchFraction = 1f / mFastScrollerSections.size();
|
|
float cumulativeTouchFraction = 0f;
|
|
for (FastScrollSectionInfo info : mFastScrollerSections) {
|
|
AdapterItem item = info.fastScrollToItem;
|
|
if (!AllAppsGridAdapter.isIconViewType(item.viewType)) {
|
|
info.touchFraction = 0f;
|
|
continue;
|
|
}
|
|
info.touchFraction = cumulativeTouchFraction;
|
|
cumulativeTouchFraction += perSectionTouchFraction;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isAppDiscoveryRunning() {
|
|
return mAppDiscoveryUpdateState == AppDiscoveryUpdateState.START
|
|
|| mAppDiscoveryUpdateState == AppDiscoveryUpdateState.UPDATE;
|
|
}
|
|
|
|
private List<AppInfo> getFiltersAppInfos() {
|
|
if (mSearchResults == null) {
|
|
return mApps;
|
|
}
|
|
|
|
ArrayList<AppInfo> result = new ArrayList<>();
|
|
for (ComponentKey key : mSearchResults) {
|
|
AppInfo match = mComponentToAppMap.get(key);
|
|
if (match != null) {
|
|
result.add(match);
|
|
}
|
|
}
|
|
|
|
// adding recently used instant apps
|
|
if (mDiscoveredApps.size() > 0) {
|
|
for (int i = 0; i < mDiscoveredApps.size(); i++) {
|
|
AppDiscoveryAppInfo discoveryAppInfo = mDiscoveredApps.get(i);
|
|
if (discoveryAppInfo.isRecent) {
|
|
result.add(discoveryAppInfo);
|
|
}
|
|
}
|
|
Collections.sort(result, mAppNameComparator);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) {
|
|
return mapper.getItem(mComponentToAppMap);
|
|
}
|
|
|
|
/**
|
|
* Returns the cached section name for the given title, recomputing and updating the cache if
|
|
* the title has no cached section name.
|
|
*/
|
|
private String getAndUpdateCachedSectionName(CharSequence title) {
|
|
String sectionName = mCachedSectionNames.get(title);
|
|
if (sectionName == null) {
|
|
sectionName = mIndexer.computeSectionName(title);
|
|
mCachedSectionNames.put(title, sectionName);
|
|
}
|
|
return sectionName;
|
|
}
|
|
|
|
}
|