2 * Copyright (C) 2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.android.launcher3.allapps;
18 import android.annotation.SuppressLint;
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.graphics.Point;
22 import android.graphics.Rect;
23 import android.support.v7.widget.RecyclerView;
24 import android.text.Selection;
25 import android.text.Spannable;
26 import android.text.SpannableString;
27 import android.text.SpannableStringBuilder;
28 import android.text.method.TextKeyListener;
29 import android.util.AttributeSet;
30 import android.view.KeyEvent;
31 import android.view.MotionEvent;
32 import android.view.View;
33 import android.view.ViewConfiguration;
34 import android.view.ViewGroup;
36 import com.android.launcher3.AppInfo;
37 import com.android.launcher3.BaseContainerView;
38 import com.android.launcher3.CellLayout;
39 import com.android.launcher3.DeleteDropTarget;
40 import com.android.launcher3.DeviceProfile;
41 import com.android.launcher3.DragSource;
42 import com.android.launcher3.DropTarget;
43 import com.android.launcher3.ExtendedEditText;
44 import com.android.launcher3.ItemInfo;
45 import com.android.launcher3.Launcher;
46 import com.android.launcher3.LauncherTransitionable;
47 import com.android.launcher3.R;
48 import com.android.launcher3.Utilities;
49 import com.android.launcher3.Workspace;
50 import com.android.launcher3.config.FeatureFlags;
51 import com.android.launcher3.folder.Folder;
52 import com.android.launcher3.graphics.TintedDrawableSpan;
53 import com.android.launcher3.keyboard.FocusedItemDecorator;
54 import com.android.launcher3.util.ComponentKey;
56 import java.nio.charset.Charset;
57 import java.nio.charset.CharsetEncoder;
58 import java.util.ArrayList;
59 import java.util.List;
63 * A merge algorithm that merges every section indiscriminately.
65 final class FullMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm {
68 public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
69 AlphabeticalAppsList.SectionInfo withSection,
70 int sectionAppCount, int numAppsPerRow, int mergeCount) {
71 // Don't merge the predicted apps
72 if (section.firstAppItem.viewType != AllAppsGridAdapter.VIEW_TYPE_ICON) {
75 // Otherwise, merge every other section
81 * The logic we use to merge multiple sections. We only merge sections when their final row
82 * contains less than a certain number of icons, and stop at a specified max number of merges.
83 * In addition, we will try and not merge sections that identify apps from different scripts.
85 final class SimpleSectionMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm {
87 private int mMinAppsPerRow;
88 private int mMinRowsInMergedSection;
89 private int mMaxAllowableMerges;
90 private CharsetEncoder mAsciiEncoder;
92 public SimpleSectionMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) {
93 mMinAppsPerRow = minAppsPerRow;
94 mMinRowsInMergedSection = minRowsInMergedSection;
95 mMaxAllowableMerges = maxNumMerges;
96 mAsciiEncoder = Charset.forName("US-ASCII").newEncoder();
100 public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
101 AlphabeticalAppsList.SectionInfo withSection,
102 int sectionAppCount, int numAppsPerRow, int mergeCount) {
103 // Don't merge the predicted apps
104 if (section.firstAppItem.viewType != AllAppsGridAdapter.VIEW_TYPE_ICON) {
108 // Continue merging if the number of hanging apps on the final row is less than some
109 // fixed number (ragged), the merged rows has yet to exceed some minimum row count,
110 // and while the number of merged sections is less than some fixed number of merges
111 int rows = sectionAppCount / numAppsPerRow;
112 int cols = sectionAppCount % numAppsPerRow;
114 // Ensure that we do not merge across scripts, currently we only allow for english and
115 // native scripts so we can test if both can just be ascii encoded
116 boolean isCrossScript = false;
117 if (section.firstAppItem != null && withSection.firstAppItem != null) {
118 isCrossScript = mAsciiEncoder.canEncode(section.firstAppItem.sectionName) !=
119 mAsciiEncoder.canEncode(withSection.firstAppItem.sectionName);
121 return (0 < cols && cols < mMinAppsPerRow) &&
122 rows < mMinRowsInMergedSection &&
123 mergeCount < mMaxAllowableMerges &&
129 * The all apps view container.
131 public class AllAppsContainerView extends BaseContainerView implements DragSource,
132 LauncherTransitionable, View.OnLongClickListener, AllAppsSearchBarController.Callbacks {
134 private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
135 private static final int MAX_NUM_MERGES_PHONE = 2;
137 private final Launcher mLauncher;
138 private final AlphabeticalAppsList mApps;
139 private final AllAppsGridAdapter mAdapter;
140 private final RecyclerView.LayoutManager mLayoutManager;
141 private final RecyclerView.ItemDecoration mItemDecoration;
143 // The computed bounds of the container
144 private final Rect mContentBounds = new Rect();
146 private AllAppsRecyclerView mAppsRecyclerView;
147 private AllAppsSearchBarController mSearchBarController;
149 private View mSearchContainer;
150 private ExtendedEditText mSearchInput;
151 private HeaderElevationController mElevationController;
152 private int mSearchContainerOffsetTop;
154 private SpannableStringBuilder mSearchQueryBuilder = null;
156 private int mSectionNamesMargin;
157 private int mNumAppsPerRow;
158 private int mNumPredictedAppsPerRow;
159 private int mRecyclerViewBottomPadding;
160 // This coordinate is relative to this container view
161 private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1);
163 public AllAppsContainerView(Context context) {
167 public AllAppsContainerView(Context context, AttributeSet attrs) {
168 this(context, attrs, 0);
171 public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
172 super(context, attrs, defStyleAttr);
173 Resources res = context.getResources();
175 mLauncher = Launcher.getLauncher(context);
176 mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
177 mApps = new AlphabeticalAppsList(context);
178 mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);
179 mApps.setAdapter(mAdapter);
180 mLayoutManager = mAdapter.getLayoutManager();
181 mItemDecoration = mAdapter.getItemDecoration();
182 DeviceProfile grid = mLauncher.getDeviceProfile();
183 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && !grid.isVerticalBarLayout()) {
184 mRecyclerViewBottomPadding = 0;
185 setPadding(0, 0, 0, 0);
187 mRecyclerViewBottomPadding =
188 res.getDimensionPixelSize(R.dimen.all_apps_list_bottom_padding);
190 mSearchQueryBuilder = new SpannableStringBuilder();
191 Selection.setSelection(mSearchQueryBuilder, 0);
195 * Sets the current set of predicted apps.
197 public void setPredictedApps(List<ComponentKey> apps) {
198 mApps.setPredictedApps(apps);
202 * Sets the current set of apps.
204 public void setApps(List<AppInfo> apps) {
209 * Adds new apps to the list.
211 public void addApps(List<AppInfo> apps) {
213 mSearchBarController.refreshSearchResult();
217 * Updates existing apps in the list
219 public void updateApps(List<AppInfo> apps) {
220 mApps.updateApps(apps);
221 mSearchBarController.refreshSearchResult();
225 * Removes some apps from the list.
227 public void removeApps(List<AppInfo> apps) {
228 mApps.removeApps(apps);
229 mSearchBarController.refreshSearchResult();
232 public void setSearchBarVisible(boolean visible) {
234 mSearchBarController.setVisibility(View.VISIBLE);
236 mSearchBarController.setVisibility(View.INVISIBLE);
241 * Sets the search bar that shows above the a-z list.
243 public void setSearchBarController(AllAppsSearchBarController searchController) {
244 if (mSearchBarController != null) {
245 throw new RuntimeException("Expected search bar controller to only be set once");
247 mSearchBarController = searchController;
248 mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
249 mAdapter.setSearchController(mSearchBarController);
253 * Scrolls this list view to the top.
255 public void scrollToTop() {
256 mAppsRecyclerView.scrollToTop();
260 * Returns whether the view itself will handle the touch event or not.
262 public boolean shouldContainerScroll(float x, float y) {
263 int[] point = new int[2];
266 Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, point);
268 // if the MotionEvent is inside the thumb, container should not be pulled down.
269 if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) {
272 // IF scroller is at the very top OR there is no scroll bar because there is probably not
273 // enough items to scroll, THEN it's okay for the container to be pulled down.
274 if (mAppsRecyclerView.getScrollBar().getThumbOffset().y <= 0) {
281 * Focuses the search field and begins an app search.
283 public void startAppsSearch() {
284 if (mSearchBarController != null) {
285 mSearchBarController.focusSearchField();
290 * Resets the state of AllApps.
292 public void reset() {
293 // Reset the search bar and base recycler view after transitioning home
295 mSearchBarController.reset();
296 mAppsRecyclerView.reset();
300 protected void onFinishInflate() {
301 super.onFinishInflate();
303 // This is a focus listener that proxies focus from a view into the list view. This is to
304 // work around the search box from getting first focus and showing the cursor.
305 getContentView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
307 public void onFocusChange(View v, boolean hasFocus) {
309 mAppsRecyclerView.requestFocus();
314 mSearchContainer = findViewById(R.id.search_container);
315 mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
317 // Update the hint to contain the icon.
318 // Prefix the original hint with two spaces. The first space gets replaced by the icon
319 // using span. The second space is used for a singe space character between the hint
321 SpannableString spanned = new SpannableString(" " + mSearchInput.getHint());
322 spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
323 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
324 mSearchInput.setHint(spanned);
326 mSearchContainerOffsetTop = getResources().getDimensionPixelSize(
327 R.dimen.all_apps_search_bar_margin_top);
329 mElevationController = Utilities.ATLEAST_LOLLIPOP
330 ? new HeaderElevationController.ControllerVL(mSearchContainer)
331 : new HeaderElevationController.ControllerV16(mSearchContainer);
333 // Load the all apps recycler view
334 mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
335 mAppsRecyclerView.setApps(mApps);
336 mAppsRecyclerView.setLayoutManager(mLayoutManager);
337 mAppsRecyclerView.setAdapter(mAdapter);
338 mAppsRecyclerView.setHasFixedSize(true);
339 mAppsRecyclerView.addOnScrollListener(mElevationController);
340 mAppsRecyclerView.setElevationController(mElevationController);
342 if (mItemDecoration != null) {
343 mAppsRecyclerView.addItemDecoration(mItemDecoration);
346 FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
347 mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
348 mAppsRecyclerView.preMeasureViews(mAdapter);
349 mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
351 // TODO(hyunyoungs): clean up setting the content and the reveal view.
352 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
353 getRevealView().setVisibility(View.VISIBLE);
358 public void onBoundsChanged(Rect newBounds) { }
361 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
362 int widthPx = MeasureSpec.getSize(widthMeasureSpec);
363 int heightPx = MeasureSpec.getSize(heightMeasureSpec);
364 updatePaddingsAndMargins(widthPx, heightPx);
365 mContentBounds.set(mContainerPaddingLeft, 0, widthPx - mContainerPaddingRight, heightPx);
367 DeviceProfile grid = mLauncher.getDeviceProfile();
368 grid.updateAppsViewNumCols();
369 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
370 if (mNumAppsPerRow != grid.inv.numColumns ||
371 mNumPredictedAppsPerRow != grid.inv.numColumns) {
372 mNumAppsPerRow = grid.inv.numColumns;
373 mNumPredictedAppsPerRow = grid.inv.numColumns;
375 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
376 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
377 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, new FullMergeAlgorithm());
378 if (mNumAppsPerRow > 0) {
379 int rvPadding = mAppsRecyclerView.getPaddingStart(); // Assumes symmetry
380 final int thumbMaxWidth =
381 getResources().getDimensionPixelSize(
382 R.dimen.container_fastscroll_thumb_max_width);
383 mSearchContainer.setPadding(
384 rvPadding - mContainerPaddingLeft + thumbMaxWidth,
385 mSearchContainer.getPaddingTop(),
386 rvPadding - mContainerPaddingRight + thumbMaxWidth,
387 mSearchContainer.getPaddingBottom());
390 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
394 // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
396 // Update the number of items in the grid before we measure the view
397 // TODO: mSectionNamesMargin is currently 0, but also account for it,
398 // if it's enabled in the future.
399 grid.updateAppsViewNumCols();
400 if (mNumAppsPerRow != grid.allAppsNumCols ||
401 mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
402 mNumAppsPerRow = grid.allAppsNumCols;
403 mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
405 // If there is a start margin to draw section names, determine how we are going to merge
407 boolean mergeSectionsFully = mSectionNamesMargin == 0 || !grid.isPhone;
408 AlphabeticalAppsList.MergeAlgorithm mergeAlgorithm = mergeSectionsFully ?
409 new FullMergeAlgorithm() :
410 new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f),
411 MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
413 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
414 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
415 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
418 // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
419 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
423 * Update the background and padding of the Apps view and children. Instead of insetting the
424 * container view, we inset the background and padding of the recycler view to allow for the
425 * recycler view to handle touch events (for fast scrolling) all the way to the edge.
427 private void updatePaddingsAndMargins(int widthPx, int heightPx) {
428 Rect bgPadding = new Rect();
429 getRevealView().getBackground().getPadding(bgPadding);
431 mAppsRecyclerView.updateBackgroundPadding(bgPadding);
432 mAdapter.updateBackgroundPadding(bgPadding);
433 mElevationController.updateBackgroundPadding(bgPadding);
435 // Pad the recycler view by the background padding plus the start margin (for the section
437 int maxScrollBarWidth = mAppsRecyclerView.getMaxScrollbarWidth();
438 int startInset = Math.max(mSectionNamesMargin, maxScrollBarWidth);
439 if (Utilities.isRtl(getResources())) {
440 mAppsRecyclerView.setPadding(bgPadding.left + maxScrollBarWidth, 0, bgPadding.right
441 + startInset, mRecyclerViewBottomPadding);
443 mAppsRecyclerView.setPadding(bgPadding.left + startInset, 0, bgPadding.right +
444 maxScrollBarWidth, mRecyclerViewBottomPadding);
447 MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
448 lp.leftMargin = bgPadding.left;
449 lp.rightMargin = bgPadding.right;
451 // Clip the view to the left and right edge of the background to
452 // to prevent shadows from rendering beyond the edges
453 final Rect newClipBounds = new Rect(
454 bgPadding.left, 0, widthPx - bgPadding.right, heightPx);
455 setClipBounds(newClipBounds);
457 // Allow the overscroll effect to reach the edges of the view
458 mAppsRecyclerView.setClipToPadding(false);
460 DeviceProfile grid = mLauncher.getDeviceProfile();
461 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
462 if (!grid.isVerticalBarLayout()) {
463 MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams();
465 Rect insets = mLauncher.getDragLayer().getInsets();
466 getContentView().setPadding(0, 0, 0, insets.bottom);
467 int height = insets.top + grid.hotseatCellHeightPx;
469 mlp.topMargin = height;
470 mAppsRecyclerView.setLayoutParams(mlp);
472 mSearchContainer.setPadding(
473 mSearchContainer.getPaddingLeft(),
474 insets.top + mSearchContainerOffsetTop,
475 mSearchContainer.getPaddingRight(),
476 mSearchContainer.getPaddingBottom());
479 View navBarBg = findViewById(R.id.nav_bar_bg);
480 ViewGroup.LayoutParams params = navBarBg.getLayoutParams();
481 params.height = insets.bottom;
482 navBarBg.setLayoutParams(params);
483 navBarBg.setVisibility(View.VISIBLE);
486 mSearchContainer.setLayoutParams(lp);
490 public boolean dispatchKeyEvent(KeyEvent event) {
491 // Determine if the key event was actual text, if so, focus the search bar and then dispatch
492 // the key normally so that it can process this key event
493 if (!mSearchBarController.isSearchFieldFocused() &&
494 event.getAction() == KeyEvent.ACTION_DOWN) {
495 final int unicodeChar = event.getUnicodeChar();
496 final boolean isKeyNotWhitespace = unicodeChar > 0 &&
497 !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
498 if (isKeyNotWhitespace) {
499 boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
500 event.getKeyCode(), event);
501 if (gotKey && mSearchQueryBuilder.length() > 0) {
502 mSearchBarController.focusSearchField();
507 return super.dispatchKeyEvent(event);
511 public boolean onInterceptTouchEvent(MotionEvent ev) {
512 return handleTouchEvent(ev);
515 @SuppressLint("ClickableViewAccessibility")
517 public boolean onTouchEvent(MotionEvent ev) {
518 return handleTouchEvent(ev);
522 public boolean onLongClick(View v) {
523 // Return early if this is not initiated from a touch
524 if (!v.isInTouchMode()) return false;
525 // When we have exited all apps or are in transition, disregard long clicks
527 if (!mLauncher.isAppsViewVisible() ||
528 mLauncher.getWorkspace().isSwitchingState()) return false;
529 // Return if global dragging is not enabled
530 if (!mLauncher.isDraggingEnabled()) return false;
533 mLauncher.getWorkspace().beginDragShared(v, this, false);
534 // Enter spring loaded mode
535 mLauncher.enterSpringLoadedDragMode();
541 public boolean supportsFlingToDelete() {
546 public boolean supportsAppInfoDropTarget() {
551 public boolean supportsDeleteDropTarget() {
556 public float getIntrinsicIconScaleFactor() {
557 DeviceProfile grid = mLauncher.getDeviceProfile();
558 return (float) grid.allAppsIconSizePx / grid.iconSizePx;
562 public void onFlingToDeleteCompleted() {
563 // We just dismiss the drag when we fling, so cleanup here
564 mLauncher.exitSpringLoadedDragModeDelayed(true,
565 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
566 mLauncher.unlockScreenOrientation(false);
570 public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
572 if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
573 !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
574 // Exit spring loaded mode if we have not successfully dropped or have not handled the
576 mLauncher.exitSpringLoadedDragModeDelayed(true,
577 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
579 mLauncher.unlockScreenOrientation(false);
581 // Display an error message if the drag failed due to there not being enough space on the
582 // target layout we were dropping on.
584 boolean showOutOfSpaceMessage = false;
585 if (target instanceof Workspace) {
586 int currentScreen = mLauncher.getCurrentWorkspaceScreen();
587 Workspace workspace = (Workspace) target;
588 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
589 ItemInfo itemInfo = d.dragInfo;
590 if (layout != null) {
591 showOutOfSpaceMessage =
592 !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
595 if (showOutOfSpaceMessage) {
596 mLauncher.showOutOfSpaceMessage(false);
599 d.deferDragViewCleanupPostAnimation = false;
604 public void onLauncherTransitionPrepare(Launcher l, boolean animated,
605 boolean multiplePagesVisible) {
610 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
615 public void onLauncherTransitionStep(Launcher l, float t) {
620 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
627 * Handles the touch events to dismiss all apps when clicking outside the bounds of the
630 private boolean handleTouchEvent(MotionEvent ev) {
631 DeviceProfile grid = mLauncher.getDeviceProfile();
632 int x = (int) ev.getX();
633 int y = (int) ev.getY();
635 switch (ev.getAction()) {
636 case MotionEvent.ACTION_DOWN:
637 if (!mContentBounds.isEmpty()) {
638 // Outset the fixed bounds and check if the touch is outside all apps
639 Rect tmpRect = new Rect(mContentBounds);
640 tmpRect.inset(-grid.allAppsIconSizePx / 2, 0);
641 if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) {
642 mBoundsCheckLastTouchDownPos.set(x, y);
646 // Check if the touch is outside all apps
647 if (ev.getX() < getPaddingLeft() ||
648 ev.getX() > (getWidth() - getPaddingRight())) {
649 mBoundsCheckLastTouchDownPos.set(x, y);
654 case MotionEvent.ACTION_UP:
655 if (mBoundsCheckLastTouchDownPos.x > -1) {
656 ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
657 float dx = ev.getX() - mBoundsCheckLastTouchDownPos.x;
658 float dy = ev.getY() - mBoundsCheckLastTouchDownPos.y;
659 float distance = (float) Math.hypot(dx, dy);
660 if (distance < viewConfig.getScaledTouchSlop()) {
661 // The background was clicked, so just go home
662 Launcher launcher = (Launcher) getContext();
663 launcher.showWorkspace(true);
668 case MotionEvent.ACTION_CANCEL:
669 mBoundsCheckLastTouchDownPos.set(-1, -1);
676 public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
678 if (mApps.setOrderedFilter(apps)) {
679 mAppsRecyclerView.onSearchResultsChanged();
681 mAdapter.setLastSearchQuery(query);
686 public void clearSearchResult() {
687 if (mApps.setOrderedFilter(null)) {
688 mAppsRecyclerView.onSearchResultsChanged();
691 // Clear the search query
692 mSearchQueryBuilder.clear();
693 mSearchQueryBuilder.clearSpans();
694 Selection.setSelection(mSearchQueryBuilder, 0);