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) {
216 * Updates existing apps in the list
218 public void updateApps(List<AppInfo> apps) {
219 mApps.updateApps(apps);
223 * Removes some apps from the list.
225 public void removeApps(List<AppInfo> apps) {
226 mApps.removeApps(apps);
229 public void setSearchBarVisible(boolean visible) {
231 mSearchBarController.setVisibility(View.VISIBLE);
233 mSearchBarController.setVisibility(View.INVISIBLE);
238 * Sets the search bar that shows above the a-z list.
240 public void setSearchBarController(AllAppsSearchBarController searchController) {
241 if (mSearchBarController != null) {
242 throw new RuntimeException("Expected search bar controller to only be set once");
244 mSearchBarController = searchController;
245 mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
246 mAdapter.setSearchController(mSearchBarController);
250 * Scrolls this list view to the top.
252 public void scrollToTop() {
253 mAppsRecyclerView.scrollToTop();
257 * Returns whether the view itself will handle the touch event or not.
259 public boolean shouldContainerScroll(float x, float y) {
260 int[] point = new int[2];
263 Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, point);
265 // if the MotionEvent is inside the thumb, container should not be pulled down.
266 if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) {
269 // IF scroller is at the very top OR there is no scroll bar because there is probably not
270 // enough items to scroll, THEN it's okay for the container to be pulled down.
271 if (mAppsRecyclerView.getScrollBar().getThumbOffset().y <= 0) {
278 * Focuses the search field and begins an app search.
280 public void startAppsSearch() {
281 if (mSearchBarController != null) {
282 mSearchBarController.focusSearchField();
287 * Resets the state of AllApps.
289 public void reset() {
290 // Reset the search bar and base recycler view after transitioning home
292 mSearchBarController.reset();
293 mAppsRecyclerView.reset();
297 protected void onFinishInflate() {
298 super.onFinishInflate();
300 // This is a focus listener that proxies focus from a view into the list view. This is to
301 // work around the search box from getting first focus and showing the cursor.
302 getContentView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
304 public void onFocusChange(View v, boolean hasFocus) {
306 mAppsRecyclerView.requestFocus();
311 mSearchContainer = findViewById(R.id.search_container);
312 mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
314 // Update the hint to contain the icon.
315 // Prefix the original hint with two spaces. The first space gets replaced by the icon
316 // using span. The second space is used for a singe space character between the hint
318 SpannableString spanned = new SpannableString(" " + mSearchInput.getHint());
319 spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
320 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
321 mSearchInput.setHint(spanned);
323 mSearchContainerOffsetTop = getResources().getDimensionPixelSize(
324 R.dimen.all_apps_search_bar_margin_top);
326 mElevationController = Utilities.ATLEAST_LOLLIPOP
327 ? new HeaderElevationController.ControllerVL(mSearchContainer)
328 : new HeaderElevationController.ControllerV16(mSearchContainer);
330 // Load the all apps recycler view
331 mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
332 mAppsRecyclerView.setApps(mApps);
333 mAppsRecyclerView.setLayoutManager(mLayoutManager);
334 mAppsRecyclerView.setAdapter(mAdapter);
335 mAppsRecyclerView.setHasFixedSize(true);
336 mAppsRecyclerView.addOnScrollListener(mElevationController);
337 mAppsRecyclerView.setElevationController(mElevationController);
339 if (mItemDecoration != null) {
340 mAppsRecyclerView.addItemDecoration(mItemDecoration);
343 FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
344 mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
345 mAppsRecyclerView.preMeasureViews(mAdapter);
346 mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
348 // TODO(hyunyoungs): clean up setting the content and the reveal view.
349 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
350 getRevealView().setVisibility(View.VISIBLE);
355 public void onBoundsChanged(Rect newBounds) { }
358 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
359 int widthPx = MeasureSpec.getSize(widthMeasureSpec);
360 int heightPx = MeasureSpec.getSize(heightMeasureSpec);
361 updatePaddingsAndMargins(widthPx, heightPx);
362 mContentBounds.set(mContainerPaddingLeft, 0, widthPx - mContainerPaddingRight, heightPx);
364 DeviceProfile grid = mLauncher.getDeviceProfile();
365 grid.updateAppsViewNumCols();
366 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
367 if (mNumAppsPerRow != grid.inv.numColumns ||
368 mNumPredictedAppsPerRow != grid.inv.numColumns) {
369 mNumAppsPerRow = grid.inv.numColumns;
370 mNumPredictedAppsPerRow = grid.inv.numColumns;
372 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
373 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
374 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, new FullMergeAlgorithm());
375 if (mNumAppsPerRow > 0) {
376 int rvPadding = mAppsRecyclerView.getPaddingStart(); // Assumes symmetry
377 final int thumbMaxWidth =
378 getResources().getDimensionPixelSize(
379 R.dimen.container_fastscroll_thumb_max_width);
380 mSearchContainer.setPadding(
381 rvPadding - mContainerPaddingLeft + thumbMaxWidth,
382 mSearchContainer.getPaddingTop(),
383 rvPadding - mContainerPaddingRight + thumbMaxWidth,
384 mSearchContainer.getPaddingBottom());
387 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
391 // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
393 // Update the number of items in the grid before we measure the view
394 // TODO: mSectionNamesMargin is currently 0, but also account for it,
395 // if it's enabled in the future.
396 grid.updateAppsViewNumCols();
397 if (mNumAppsPerRow != grid.allAppsNumCols ||
398 mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
399 mNumAppsPerRow = grid.allAppsNumCols;
400 mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
402 // If there is a start margin to draw section names, determine how we are going to merge
404 boolean mergeSectionsFully = mSectionNamesMargin == 0 || !grid.isPhone;
405 AlphabeticalAppsList.MergeAlgorithm mergeAlgorithm = mergeSectionsFully ?
406 new FullMergeAlgorithm() :
407 new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f),
408 MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
410 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
411 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
412 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
415 // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
416 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
420 * Update the background and padding of the Apps view and children. Instead of insetting the
421 * container view, we inset the background and padding of the recycler view to allow for the
422 * recycler view to handle touch events (for fast scrolling) all the way to the edge.
424 private void updatePaddingsAndMargins(int widthPx, int heightPx) {
425 Rect bgPadding = new Rect();
426 getRevealView().getBackground().getPadding(bgPadding);
428 mAppsRecyclerView.updateBackgroundPadding(bgPadding);
429 mAdapter.updateBackgroundPadding(bgPadding);
430 mElevationController.updateBackgroundPadding(bgPadding);
432 // Pad the recycler view by the background padding plus the start margin (for the section
434 int maxScrollBarWidth = mAppsRecyclerView.getMaxScrollbarWidth();
435 int startInset = Math.max(mSectionNamesMargin, maxScrollBarWidth);
436 if (Utilities.isRtl(getResources())) {
437 mAppsRecyclerView.setPadding(bgPadding.left + maxScrollBarWidth, 0, bgPadding.right
438 + startInset, mRecyclerViewBottomPadding);
440 mAppsRecyclerView.setPadding(bgPadding.left + startInset, 0, bgPadding.right +
441 maxScrollBarWidth, mRecyclerViewBottomPadding);
444 MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
445 lp.leftMargin = bgPadding.left;
446 lp.rightMargin = bgPadding.right;
448 // Clip the view to the left and right edge of the background to
449 // to prevent shadows from rendering beyond the edges
450 final Rect newClipBounds = new Rect(
451 bgPadding.left, 0, widthPx - bgPadding.right, heightPx);
452 setClipBounds(newClipBounds);
454 // Allow the overscroll effect to reach the edges of the view
455 mAppsRecyclerView.setClipToPadding(false);
457 DeviceProfile grid = mLauncher.getDeviceProfile();
458 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
459 if (!grid.isVerticalBarLayout()) {
460 MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams();
462 Rect insets = mLauncher.getDragLayer().getInsets();
463 getContentView().setPadding(0, 0, 0, insets.bottom);
464 int height = insets.top + grid.hotseatCellHeightPx;
466 mlp.topMargin = height;
467 mAppsRecyclerView.setLayoutParams(mlp);
469 mSearchContainer.setPadding(
470 mSearchContainer.getPaddingLeft(),
471 insets.top + mSearchContainerOffsetTop,
472 mSearchContainer.getPaddingRight(),
473 mSearchContainer.getPaddingBottom());
476 View navBarBg = findViewById(R.id.nav_bar_bg);
477 ViewGroup.LayoutParams params = navBarBg.getLayoutParams();
478 params.height = insets.bottom;
479 navBarBg.setLayoutParams(params);
480 navBarBg.setVisibility(View.VISIBLE);
483 mSearchContainer.setLayoutParams(lp);
487 public boolean dispatchKeyEvent(KeyEvent event) {
488 // Determine if the key event was actual text, if so, focus the search bar and then dispatch
489 // the key normally so that it can process this key event
490 if (!mSearchBarController.isSearchFieldFocused() &&
491 event.getAction() == KeyEvent.ACTION_DOWN) {
492 final int unicodeChar = event.getUnicodeChar();
493 final boolean isKeyNotWhitespace = unicodeChar > 0 &&
494 !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
495 if (isKeyNotWhitespace) {
496 boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
497 event.getKeyCode(), event);
498 if (gotKey && mSearchQueryBuilder.length() > 0) {
499 mSearchBarController.focusSearchField();
504 return super.dispatchKeyEvent(event);
508 public boolean onInterceptTouchEvent(MotionEvent ev) {
509 return handleTouchEvent(ev);
512 @SuppressLint("ClickableViewAccessibility")
514 public boolean onTouchEvent(MotionEvent ev) {
515 return handleTouchEvent(ev);
519 public boolean onLongClick(View v) {
520 // Return early if this is not initiated from a touch
521 if (!v.isInTouchMode()) return false;
522 // When we have exited all apps or are in transition, disregard long clicks
524 if (!mLauncher.isAppsViewVisible() ||
525 mLauncher.getWorkspace().isSwitchingState()) return false;
526 // Return if global dragging is not enabled
527 if (!mLauncher.isDraggingEnabled()) return false;
530 mLauncher.getWorkspace().beginDragShared(v, this, false);
531 // Enter spring loaded mode
532 mLauncher.enterSpringLoadedDragMode();
538 public boolean supportsFlingToDelete() {
543 public boolean supportsAppInfoDropTarget() {
548 public boolean supportsDeleteDropTarget() {
553 public float getIntrinsicIconScaleFactor() {
554 DeviceProfile grid = mLauncher.getDeviceProfile();
555 return (float) grid.allAppsIconSizePx / grid.iconSizePx;
559 public void onFlingToDeleteCompleted() {
560 // We just dismiss the drag when we fling, so cleanup here
561 mLauncher.exitSpringLoadedDragModeDelayed(true,
562 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
563 mLauncher.unlockScreenOrientation(false);
567 public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
569 if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
570 !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
571 // Exit spring loaded mode if we have not successfully dropped or have not handled the
573 mLauncher.exitSpringLoadedDragModeDelayed(true,
574 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
576 mLauncher.unlockScreenOrientation(false);
578 // Display an error message if the drag failed due to there not being enough space on the
579 // target layout we were dropping on.
581 boolean showOutOfSpaceMessage = false;
582 if (target instanceof Workspace) {
583 int currentScreen = mLauncher.getCurrentWorkspaceScreen();
584 Workspace workspace = (Workspace) target;
585 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
586 ItemInfo itemInfo = d.dragInfo;
587 if (layout != null) {
588 showOutOfSpaceMessage =
589 !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
592 if (showOutOfSpaceMessage) {
593 mLauncher.showOutOfSpaceMessage(false);
596 d.deferDragViewCleanupPostAnimation = false;
601 public void onLauncherTransitionPrepare(Launcher l, boolean animated,
602 boolean multiplePagesVisible) {
607 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
612 public void onLauncherTransitionStep(Launcher l, float t) {
617 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
624 * Handles the touch events to dismiss all apps when clicking outside the bounds of the
627 private boolean handleTouchEvent(MotionEvent ev) {
628 DeviceProfile grid = mLauncher.getDeviceProfile();
629 int x = (int) ev.getX();
630 int y = (int) ev.getY();
632 switch (ev.getAction()) {
633 case MotionEvent.ACTION_DOWN:
634 if (!mContentBounds.isEmpty()) {
635 // Outset the fixed bounds and check if the touch is outside all apps
636 Rect tmpRect = new Rect(mContentBounds);
637 tmpRect.inset(-grid.allAppsIconSizePx / 2, 0);
638 if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) {
639 mBoundsCheckLastTouchDownPos.set(x, y);
643 // Check if the touch is outside all apps
644 if (ev.getX() < getPaddingLeft() ||
645 ev.getX() > (getWidth() - getPaddingRight())) {
646 mBoundsCheckLastTouchDownPos.set(x, y);
651 case MotionEvent.ACTION_UP:
652 if (mBoundsCheckLastTouchDownPos.x > -1) {
653 ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
654 float dx = ev.getX() - mBoundsCheckLastTouchDownPos.x;
655 float dy = ev.getY() - mBoundsCheckLastTouchDownPos.y;
656 float distance = (float) Math.hypot(dx, dy);
657 if (distance < viewConfig.getScaledTouchSlop()) {
658 // The background was clicked, so just go home
659 Launcher launcher = (Launcher) getContext();
660 launcher.showWorkspace(true);
665 case MotionEvent.ACTION_CANCEL:
666 mBoundsCheckLastTouchDownPos.set(-1, -1);
673 public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
675 if (mApps.setOrderedFilter(apps)) {
676 mAppsRecyclerView.onSearchResultsChanged();
678 mAdapter.setLastSearchQuery(query);
683 public void clearSearchResult() {
684 if (mApps.setOrderedFilter(null)) {
685 mAppsRecyclerView.onSearchResultsChanged();
688 // Clear the search query
689 mSearchQueryBuilder.clear();
690 mSearchQueryBuilder.clearSpans();
691 Selection.setSelection(mSearchQueryBuilder, 0);