summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3
diff options
context:
space:
mode:
authorJoey <joey@lineageos.org>2018-10-10 08:44:12 +0200
committerBruno Martins <bgcngm@gmail.com>2018-11-13 11:42:00 +0000
commit1cd02db60e0562a38181344c63d8267eb9264c68 (patch)
treed4ba743c5d1bba367787180e3374e099cc8bf5a6 /src/com/android/launcher3
parent530e3021529a8dc0740add3480545bd0cb8e66c9 (diff)
downloadandroid_packages_apps_Trebuchet-1cd02db60e0562a38181344c63d8267eb9264c68.tar.gz
android_packages_apps_Trebuchet-1cd02db60e0562a38181344c63d8267eb9264c68.tar.bz2
android_packages_apps_Trebuchet-1cd02db60e0562a38181344c63d8267eb9264c68.zip
Revert "Removing support for app prediction from Launcher3"
This reverts commit b1d222e9a76cc733cead3343a4b7c1e39daa190c. Change-Id: I5d7ed938c9817527c251a08b57a7f1e6564f3e28
Diffstat (limited to 'src/com/android/launcher3')
-rw-r--r--src/com/android/launcher3/InvariantDeviceProfile.java15
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java80
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java32
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java29
-rw-r--r--src/com/android/launcher3/allapps/AllAppsStore.java5
-rw-r--r--src/com/android/launcher3/allapps/AlphabeticalAppsList.java166
-rw-r--r--src/com/android/launcher3/config/BaseFlags.java2
-rw-r--r--src/com/android/launcher3/util/ComponentKeyMapper.java48
8 files changed, 355 insertions, 22 deletions
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 2e6abb8ac..1faa0731a 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -72,6 +72,12 @@ public class InvariantDeviceProfile {
public int numColumns;
/**
+ * The minimum number of predicted apps in all apps.
+ */
+ //@Deprecated
+ int minAllAppsPredictionColumns;
+
+ /**
* Number of icons per row and column in the folder.
*/
public int numFolderRows;
@@ -101,13 +107,13 @@ public class InvariantDeviceProfile {
private InvariantDeviceProfile(Context context, InvariantDeviceProfile p) {
this(context, p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
- p.numFolderRows, p.numFolderColumns,
+ p.numFolderRows, p.numFolderColumns, p.numHotseatIcons,
p.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
p.defaultLayoutId, p.demoModeLayoutId);
}
- private InvariantDeviceProfile(Context context, String n, float w, float h, int r, int c, int fr, int fc,
- float is, float lis, float its, int hs, int dlId, int dmlId) {
+ InvariantDeviceProfile(Context context, String n, float w, float h, int r, int c, int fr, int fc,
+ int maapc, float is, float lis, float its, int hs, int dlId, int dmlId) {
name = n;
minWidthDps = w;
minHeightDps = h;
@@ -115,6 +121,7 @@ public class InvariantDeviceProfile {
numColumns = c;
numFolderRows = fr;
numFolderColumns = fc;
+ minAllAppsPredictionColumns = maapc;
iconSize = is;
landscapeIconSize = lis;
iconTextSize = its;
@@ -153,6 +160,7 @@ public class InvariantDeviceProfile {
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
+ minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
iconSize = interpolatedDeviceProfileOut.iconSize;
landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize;
@@ -210,6 +218,7 @@ public class InvariantDeviceProfile {
numColumns,
a.getInt(R.styleable.InvariantDeviceProfile_numFolderRows, numRows),
a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
+ a.getInt(R.styleable.InvariantDeviceProfile_minAllAppsPredictionColumns, numColumns),
iconSize,
a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index fdf32af6d..d3aede050 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -51,12 +51,16 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BottomUserEducationView;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
+import java.util.List;
+
/**
* The all apps view container.
*/
@@ -83,7 +87,11 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
private SpannableStringBuilder mSearchQueryBuilder = null;
+ private int mNumAppsPerRow;
+ private int mNumPredictedAppsPerRow;
+
private boolean mUsingTabs;
+ private boolean mHasPredictions = false;
private boolean mSearchModeWhileUsingTabs = false;
private RecyclerViewFastScroller mTouchHandler;
@@ -262,6 +270,21 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+
+ if (mNumAppsPerRow != grid.inv.numColumns ||
+ mNumPredictedAppsPerRow != grid.inv.numColumns) {
+ mNumAppsPerRow = grid.inv.numColumns;
+ mNumPredictedAppsPerRow = grid.inv.numColumns;
+ for (int i = 0; i < mAH.length; i++) {
+ mAH[i].applyNumsPerRow();
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
public boolean dispatchKeyEvent(KeyEvent event) {
mSearchUiManager.preDispatchKeyEvent(event);
return super.dispatchKeyEvent(event);
@@ -336,11 +359,16 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
onTabChanged(mViewPager.getNextPage());
+ setupHeader();
} else {
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
mAH[AdapterHolder.WORK].recyclerView = null;
+ if (FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW) {
+ setupHeader();
+ } else {
+ mHeader.setVisibility(View.GONE);
+ }
}
- setupHeader();
mAllAppsStore.registerIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
@@ -388,10 +416,29 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
}
}
+ public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
+ mAH[AdapterHolder.MAIN].appsList.setPredictedApps(apps);
+ boolean hasPredictions = !apps.isEmpty();
+ if (mHasPredictions != hasPredictions) {
+ mHasPredictions = hasPredictions;
+ if (FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW) {
+ setupHeader();
+ }
+ }
+ }
+
+ public AppInfo findApp(ComponentKey mapper) {
+ return mAllAppsStore.getApp(mapper);
+ }
+
public AlphabeticalAppsList getApps() {
return mAH[AdapterHolder.MAIN].appsList;
}
+ public boolean isUsingTabs() {
+ return mUsingTabs;
+ }
+
public FloatingHeaderView getFloatingHeaderView() {
return mHeader;
}
@@ -410,12 +457,18 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
}
public void setupHeader() {
+ if (mHeader == null) {
+ return;
+ }
mHeader.setVisibility(View.VISIBLE);
mHeader.setup(mAH, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null);
int padding = mHeader.getMaxTranslation();
+ if (mHasPredictions && !mUsingTabs) {
+ padding += mHeader.getPaddingTop() + mHeader.getPaddingBottom();
+ }
for (int i = 0; i < mAH.length; i++) {
- mAH[i].padding.top = padding;
+ mAH[i].paddingTopForTabs = padding;
mAH[i].applyPadding();
}
}
@@ -445,6 +498,13 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
}
}
+ public void setRecyclerViewPaddingTop(int top) {
+ for (int i = 0; i < mAH.length; i++) {
+ mAH[i].padding.top = top;
+ mAH[i].applyPadding();
+ }
+ }
+
public void setRecyclerViewVerticalFadingEdgeEnabled(boolean enabled) {
for (int i = 0; i < mAH.length; i++) {
mAH[i].applyVerticalFadingEdgeEnabled(enabled);
@@ -510,6 +570,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
final LinearLayoutManager layoutManager;
final AlphabeticalAppsList appsList;
final Rect padding = new Rect();
+ int paddingTopForTabs;
AllAppsRecyclerView recyclerView;
boolean verticalFadingEdge;
@@ -535,11 +596,24 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
applyVerticalFadingEdgeEnabled(verticalFadingEdge);
applyPadding();
+ applyNumsPerRow();
}
void applyPadding() {
if (recyclerView != null) {
- recyclerView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ int paddingTop = mUsingTabs || FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW
+ ? paddingTopForTabs : padding.top;
+ recyclerView.setPadding(padding.left, paddingTop, padding.right, padding.bottom);
+ }
+ }
+
+ void applyNumsPerRow() {
+ if (mNumAppsPerRow > 0) {
+ if (recyclerView != null) {
+ recyclerView.setNumAppsPerRow(mLauncher.getDeviceProfile(), mNumAppsPerRow);
+ }
+ adapter.setNumAppsPerRow(mNumAppsPerRow);
+ appsList.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 27fc53a82..ff47473d2 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -53,21 +53,27 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
// A normal icon
public static final int VIEW_TYPE_ICON = 1 << 1;
+ // A prediction icon
+ public static final int VIEW_TYPE_PREDICTION_ICON = 1 << 2;
// The message shown when there are no filtered results
- public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
+ public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 3;
// The message to continue to a market search when there are no filtered results
- public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3;
+ public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 4;
// We use various dividers for various purposes. They share enough attributes to reuse layouts,
// but differ in enough attributes to require different view types
// A divider that separates the apps list and the search market button
- public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
- public static final int VIEW_TYPE_WORK_TAB_FOOTER = 1 << 5;
+ public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 5;
+ // The divider that separates prediction icons from the app list
+ public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 6;
+ public static final int VIEW_TYPE_WORK_TAB_FOOTER = 1 << 7;
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
+ public static final int VIEW_TYPE_MASK_HAS_SPRINGS = VIEW_TYPE_MASK_ICON
+ | VIEW_TYPE_PREDICTION_DIVIDER;
public interface BindViewCallback {
@@ -179,7 +185,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
private final GridLayoutManager mGridLayoutMgr;
private final GridSpanSizer mGridSizer;
- private final int mAppsPerRow;
+ private int mAppsPerRow;
private BindViewCallback mBindViewCallback;
private OnFocusChangeListener mIconFocusListener;
@@ -215,6 +221,18 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
return (viewType & viewTypeMask) != 0;
}
+ /**
+ * Sets the number of apps per row.
+ */
+ public void setNumAppsPerRow(int appsPerRow) {
+ mAppsPerRow = appsPerRow;
+ mGridLayoutMgr.setSpanCount(appsPerRow);
+ }
+
+ public int getNumAppsPerRow() {
+ return mAppsPerRow;
+ }
+
public void setIconFocusListener(OnFocusChangeListener focusListener) {
mIconFocusListener = focusListener;
}
@@ -247,6 +265,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_ICON:
+ case VIEW_TYPE_PREDICTION_ICON:
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_icon, parent, false);
icon.setOnClickListener(ItemClickHandler.INSTANCE);
@@ -270,6 +289,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
}
});
return new ViewHolder(searchMarketView);
+ case VIEW_TYPE_PREDICTION_DIVIDER:
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
@@ -285,6 +305,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON:
+ case VIEW_TYPE_PREDICTION_ICON:
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset();
@@ -338,5 +359,4 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
return item.viewType;
}
-
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index a6c1346f6..647dd0af2 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -28,13 +28,14 @@ import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -48,7 +49,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
private AlphabeticalAppsList mApps;
private AllAppsFastScrollHelper mFastScrollHelper;
- private final int mNumAppsPerRow;
+ private int mNumAppsPerRow;
// The specific view heights that we use to calculate scroll
private SparseIntArray mViewHeights = new SparseIntArray();
@@ -91,15 +92,19 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
return mApps;
}
- private void updatePoolSize() {
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+ /**
+ * Sets the number of apps per row in this recycler view.
+ */
+ public void setNumAppsPerRow(DeviceProfile grid, int numAppsPerRow) {
+ mNumAppsPerRow = numAppsPerRow;
RecyclerView.RecycledViewPool pool = getRecycledViewPool();
int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow);
-
+ pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, mNumAppsPerRow);
+ pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, 1);
mViewHeights.clear();
mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
}
@@ -133,7 +138,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
updateEmptySearchBackgroundBounds();
- updatePoolSize();
}
@Override
@@ -141,6 +145,19 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
if (mApps.hasFilter()) {
targetParent.containerType = ContainerType.SEARCHRESULT;
} else {
+ if (v instanceof BubbleTextView) {
+ BubbleTextView icon = (BubbleTextView) v;
+ int position = getChildPosition(icon);
+ if (position != NO_POSITION) {
+ List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+ AlphabeticalAppsList.AdapterItem item = items.get(position);
+ if (item.viewType == AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON) {
+ targetParent.containerType = ContainerType.PREDICTION;
+ target.predictedRank = item.rowAppIndex;
+ return;
+ }
+ }
+ }
targetParent.containerType = ContainerType.ALLAPPS;
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index dc3489290..35914d806 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -24,6 +24,7 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.ComponentKeyMapper;
import java.util.ArrayList;
import java.util.Collection;
@@ -71,6 +72,10 @@ public class AllAppsStore {
}
}
+ public AppInfo getApp(ComponentKeyMapper<AppInfo> mapper) {
+ return mapper.getItem(mComponentToAppMap);
+ }
+
/**
* Adds or updates existing apps in the list
*/
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 434918d2a..29b32b06c 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -17,13 +17,19 @@ package com.android.launcher3.allapps;
import android.content.Context;
import android.content.pm.PackageManager;
+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.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LabelComparator;
@@ -41,6 +47,8 @@ import java.util.TreeMap;
public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
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;
@@ -87,6 +95,13 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
// 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();
@@ -105,6 +120,13 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
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 asAllAppsDivider(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
@@ -129,7 +151,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
private final Launcher mLauncher;
- // The set of apps from the system
+ // The set of apps from the system not including predictions
private final List<AppInfo> mApps = new ArrayList<>();
private final AllAppsStore mAllAppsStore;
@@ -139,6 +161,10 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
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<>();
// Is it the work profile app list.
private final boolean mIsWork;
@@ -148,7 +174,8 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
private AllAppsGridAdapter mAdapter;
private AlphabeticIndexCompat mIndexer;
private AppInfoComparator mAppNameComparator;
- private final int mNumAppsPerRow;
+ private int mNumAppsPerRow;
+ private int mNumPredictedAppsPerRow;
private int mNumAppRowsInAdapter;
private ItemInfoMatcher mItemFilter;
@@ -158,7 +185,6 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppInfoComparator(context);
mIsWork = isWork;
- mNumAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
mAllAppsStore.addUpdateListener(this);
}
@@ -168,6 +194,16 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
}
/**
+ * 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) {
@@ -182,6 +218,13 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
}
/**
+ * Returns the predicted apps.
+ */
+ public List<AppInfo> getPredictedApps() {
+ return mPredictedApps;
+ }
+
+ /**
* Returns fast scroller sections of all the current filtered applications.
*/
public List<FastScrollSectionInfo> getFastScrollerSections() {
@@ -196,7 +239,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
}
/**
- * Returns the number of rows of applications
+ * Returns the number of rows of applications (not including predictions)
*/
public int getNumAppRows() {
return mNumAppRowsInAdapter;
@@ -236,6 +279,80 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
return false;
}
+ private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) {
+ if (mAllAppsStore.getApps().isEmpty()) {
+ // Apps have not been bound yet.
+ return Collections.emptyList();
+ }
+
+ List<AppInfo> predictedApps = new ArrayList<>();
+ for (ComponentKeyMapper<AppInfo> mapper : components) {
+ AppInfo info = mAllAppsStore.getApp(mapper);
+ 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 orgItem = mAdapterItems.get(i);
+ AdapterItem newItem = AdapterItem.asPredictedApp(orgItem.position, "", info,
+ orgItem.appIndex);
+ newItem.rowAppIndex = orgItem.rowAppIndex;
+
+ mAdapterItems.set(i, newItem);
+ mFilteredApps.set(i, info);
+ mAdapter.notifyItemChanged(i);
+ }
+ }
+
/**
* Updates internals when the set of apps are updated.
*/
@@ -316,6 +433,47 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
mFastScrollerSections.clear();
mAdapterItems.clear();
+ if (!FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW) {
+ 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()) {
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 8e6b27b16..90ac1869c 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -51,6 +51,8 @@ abstract class BaseFlags {
// When enabled shows a work profile tab in all apps
public static final boolean ALL_APPS_TABS_ENABLED = true;
+ // When enabled prediction row is rendered as it's own custom view
+ public static final boolean ALL_APPS_PREDICTION_ROW_VIEW = true;
// When true, overview shows screenshots in the orientation they were taken rather than
// trying to make them fit the orientation the device is in.
diff --git a/src/com/android/launcher3/util/ComponentKeyMapper.java b/src/com/android/launcher3/util/ComponentKeyMapper.java
new file mode 100644
index 000000000..a7f0d764e
--- /dev/null
+++ b/src/com/android/launcher3/util/ComponentKeyMapper.java
@@ -0,0 +1,48 @@
+package com.android.launcher3.util;
+
+/**
+ * Copyright (C) 2017 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.
+ */
+
+import android.support.annotation.Nullable;
+
+import java.util.Map;
+
+public class ComponentKeyMapper<T> {
+
+ protected final ComponentKey mComponentKey;
+
+ public ComponentKeyMapper(ComponentKey key) {
+ this.mComponentKey = key;
+ }
+
+ public @Nullable T getItem(Map<ComponentKey, T> map) {
+ return map.get(mComponentKey);
+ }
+
+ public String getPackage() {
+ return mComponentKey.componentName.getPackageName();
+ }
+
+ public String getComponentClass() {
+ return mComponentKey.componentName.getClassName();
+ }
+
+ @Override
+ public String toString() {
+ return mComponentKey.toString();
+ }
+
+}