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 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
352 getRevealView().setVisibility(View.VISIBLE);
353 getContentView().setVisibility(View.VISIBLE);
354 getContentView().setBackground(null);
359 public void onBoundsChanged(Rect newBounds) { }
362 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
363 int widthPx = MeasureSpec.getSize(widthMeasureSpec);
364 int heightPx = MeasureSpec.getSize(heightMeasureSpec);
365 updatePaddingsAndMargins(widthPx, heightPx);
366 mContentBounds.set(mContainerPaddingLeft, 0, widthPx - mContainerPaddingRight, heightPx);
368 DeviceProfile grid = mLauncher.getDeviceProfile();
369 grid.updateAppsViewNumCols();
370 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
371 if (mNumAppsPerRow != grid.inv.numColumns ||
372 mNumPredictedAppsPerRow != grid.inv.numColumns) {
373 mNumAppsPerRow = grid.inv.numColumns;
374 mNumPredictedAppsPerRow = grid.inv.numColumns;
376 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
377 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
378 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, new FullMergeAlgorithm());
379 if (mNumAppsPerRow > 0) {
380 int rvPadding = mAppsRecyclerView.getPaddingStart(); // Assumes symmetry
381 final int thumbMaxWidth =
382 getResources().getDimensionPixelSize(
383 R.dimen.container_fastscroll_thumb_max_width);
384 mSearchContainer.setPadding(
385 rvPadding - mContainerPaddingLeft + thumbMaxWidth,
386 mSearchContainer.getPaddingTop(),
387 rvPadding - mContainerPaddingRight + thumbMaxWidth,
388 mSearchContainer.getPaddingBottom());
391 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
395 // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
397 // Update the number of items in the grid before we measure the view
398 // TODO: mSectionNamesMargin is currently 0, but also account for it,
399 // if it's enabled in the future.
400 grid.updateAppsViewNumCols();
401 if (mNumAppsPerRow != grid.allAppsNumCols ||
402 mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
403 mNumAppsPerRow = grid.allAppsNumCols;
404 mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
406 // If there is a start margin to draw section names, determine how we are going to merge
408 boolean mergeSectionsFully = mSectionNamesMargin == 0 || !grid.isPhone;
409 AlphabeticalAppsList.MergeAlgorithm mergeAlgorithm = mergeSectionsFully ?
410 new FullMergeAlgorithm() :
411 new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f),
412 MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
414 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
415 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
416 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
419 // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
420 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
424 * Update the background and padding of the Apps view and children. Instead of insetting the
425 * container view, we inset the background and padding of the recycler view to allow for the
426 * recycler view to handle touch events (for fast scrolling) all the way to the edge.
428 private void updatePaddingsAndMargins(int widthPx, int heightPx) {
429 Rect bgPadding = new Rect();
430 getRevealView().getBackground().getPadding(bgPadding);
432 mAppsRecyclerView.updateBackgroundPadding(bgPadding);
433 mAdapter.updateBackgroundPadding(bgPadding);
434 mElevationController.updateBackgroundPadding(bgPadding);
436 // Pad the recycler view by the background padding plus the start margin (for the section
438 int maxScrollBarWidth = mAppsRecyclerView.getMaxScrollbarWidth();
439 int startInset = Math.max(mSectionNamesMargin, maxScrollBarWidth);
440 if (Utilities.isRtl(getResources())) {
441 mAppsRecyclerView.setPadding(bgPadding.left + maxScrollBarWidth, 0, bgPadding.right
442 + startInset, mRecyclerViewBottomPadding);
444 mAppsRecyclerView.setPadding(bgPadding.left + startInset, 0, bgPadding.right +
445 maxScrollBarWidth, mRecyclerViewBottomPadding);
448 MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
449 lp.leftMargin = bgPadding.left;
450 lp.rightMargin = bgPadding.right;
452 // Clip the view to the left and right edge of the background to
453 // to prevent shadows from rendering beyond the edges
454 final Rect newClipBounds = new Rect(
455 bgPadding.left, 0, widthPx - bgPadding.right, heightPx);
456 setClipBounds(newClipBounds);
458 // Allow the overscroll effect to reach the edges of the view
459 mAppsRecyclerView.setClipToPadding(false);
461 DeviceProfile grid = mLauncher.getDeviceProfile();
462 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
463 if (!grid.isVerticalBarLayout()) {
464 MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams();
466 Rect insets = mLauncher.getDragLayer().getInsets();
467 getContentView().setPadding(0, 0, 0, 0);
468 int height = insets.top + grid.hotseatCellHeightPx;
470 mlp.topMargin = height;
471 mAppsRecyclerView.setLayoutParams(mlp);
473 mSearchContainer.setPadding(
474 mSearchContainer.getPaddingLeft(),
475 insets.top + mSearchContainerOffsetTop,
476 mSearchContainer.getPaddingRight(),
477 mSearchContainer.getPaddingBottom());
480 View navBarBg = findViewById(R.id.nav_bar_bg);
481 ViewGroup.LayoutParams params = navBarBg.getLayoutParams();
482 params.height = insets.bottom;
483 navBarBg.setLayoutParams(params);
484 navBarBg.setVisibility(View.VISIBLE);
487 mSearchContainer.setLayoutParams(lp);
491 public boolean dispatchKeyEvent(KeyEvent event) {
492 // Determine if the key event was actual text, if so, focus the search bar and then dispatch
493 // the key normally so that it can process this key event
494 if (!mSearchBarController.isSearchFieldFocused() &&
495 event.getAction() == KeyEvent.ACTION_DOWN) {
496 final int unicodeChar = event.getUnicodeChar();
497 final boolean isKeyNotWhitespace = unicodeChar > 0 &&
498 !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
499 if (isKeyNotWhitespace) {
500 boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
501 event.getKeyCode(), event);
502 if (gotKey && mSearchQueryBuilder.length() > 0) {
503 mSearchBarController.focusSearchField();
508 return super.dispatchKeyEvent(event);
512 public boolean onInterceptTouchEvent(MotionEvent ev) {
513 return handleTouchEvent(ev);
516 @SuppressLint("ClickableViewAccessibility")
518 public boolean onTouchEvent(MotionEvent ev) {
519 return handleTouchEvent(ev);
523 public boolean onLongClick(View v) {
524 // Return early if this is not initiated from a touch
525 if (!v.isInTouchMode()) return false;
526 // When we have exited all apps or are in transition, disregard long clicks
528 if (!mLauncher.isAppsViewVisible() ||
529 mLauncher.getWorkspace().isSwitchingState()) return false;
530 // Return if global dragging is not enabled
531 if (!mLauncher.isDraggingEnabled()) return false;
534 mLauncher.getWorkspace().beginDragShared(v, this, false);
535 // Enter spring loaded mode
536 mLauncher.enterSpringLoadedDragMode();
542 public boolean supportsFlingToDelete() {
547 public boolean supportsAppInfoDropTarget() {
552 public boolean supportsDeleteDropTarget() {
557 public float getIntrinsicIconScaleFactor() {
558 DeviceProfile grid = mLauncher.getDeviceProfile();
559 return (float) grid.allAppsIconSizePx / grid.iconSizePx;
563 public void onFlingToDeleteCompleted() {
564 // We just dismiss the drag when we fling, so cleanup here
565 mLauncher.exitSpringLoadedDragModeDelayed(true,
566 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
567 mLauncher.unlockScreenOrientation(false);
571 public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
573 if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
574 !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
575 // Exit spring loaded mode if we have not successfully dropped or have not handled the
577 mLauncher.exitSpringLoadedDragModeDelayed(true,
578 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
580 mLauncher.unlockScreenOrientation(false);
582 // Display an error message if the drag failed due to there not being enough space on the
583 // target layout we were dropping on.
585 boolean showOutOfSpaceMessage = false;
586 if (target instanceof Workspace) {
587 int currentScreen = mLauncher.getCurrentWorkspaceScreen();
588 Workspace workspace = (Workspace) target;
589 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
590 ItemInfo itemInfo = d.dragInfo;
591 if (layout != null) {
592 showOutOfSpaceMessage =
593 !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
596 if (showOutOfSpaceMessage) {
597 mLauncher.showOutOfSpaceMessage(false);
600 d.deferDragViewCleanupPostAnimation = false;
605 public void onLauncherTransitionPrepare(Launcher l, boolean animated,
606 boolean multiplePagesVisible) {
611 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
616 public void onLauncherTransitionStep(Launcher l, float t) {
621 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
628 * Handles the touch events to dismiss all apps when clicking outside the bounds of the
631 private boolean handleTouchEvent(MotionEvent ev) {
632 DeviceProfile grid = mLauncher.getDeviceProfile();
633 int x = (int) ev.getX();
634 int y = (int) ev.getY();
636 switch (ev.getAction()) {
637 case MotionEvent.ACTION_DOWN:
638 if (!mContentBounds.isEmpty()) {
639 // Outset the fixed bounds and check if the touch is outside all apps
640 Rect tmpRect = new Rect(mContentBounds);
641 tmpRect.inset(-grid.allAppsIconSizePx / 2, 0);
642 if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) {
643 mBoundsCheckLastTouchDownPos.set(x, y);
647 // Check if the touch is outside all apps
648 if (ev.getX() < getPaddingLeft() ||
649 ev.getX() > (getWidth() - getPaddingRight())) {
650 mBoundsCheckLastTouchDownPos.set(x, y);
655 case MotionEvent.ACTION_UP:
656 if (mBoundsCheckLastTouchDownPos.x > -1) {
657 ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
658 float dx = ev.getX() - mBoundsCheckLastTouchDownPos.x;
659 float dy = ev.getY() - mBoundsCheckLastTouchDownPos.y;
660 float distance = (float) Math.hypot(dx, dy);
661 if (distance < viewConfig.getScaledTouchSlop()) {
662 // The background was clicked, so just go home
663 Launcher launcher = (Launcher) getContext();
664 launcher.showWorkspace(true);
669 case MotionEvent.ACTION_CANCEL:
670 mBoundsCheckLastTouchDownPos.set(-1, -1);
677 public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
679 if (mApps.setOrderedFilter(apps)) {
680 mAppsRecyclerView.onSearchResultsChanged();
682 mAdapter.setLastSearchQuery(query);
687 public void clearSearchResult() {
688 if (mApps.setOrderedFilter(null)) {
689 mAppsRecyclerView.onSearchResultsChanged();
692 // Clear the search query
693 mSearchQueryBuilder.clear();
694 mSearchQueryBuilder.clearSpans();
695 Selection.setSelection(mSearchQueryBuilder, 0);