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.SpannableStringBuilder;
26 import android.text.method.TextKeyListener;
27 import android.util.AttributeSet;
28 import android.view.Gravity;
29 import android.view.KeyEvent;
30 import android.view.LayoutInflater;
31 import android.view.MotionEvent;
32 import android.view.View;
33 import android.view.ViewConfiguration;
34 import android.view.inputmethod.InputMethodManager;
35 import android.widget.ImageView;
36 import android.widget.LinearLayout;
38 import com.android.launcher3.AppInfo;
39 import com.android.launcher3.BaseContainerView;
40 import com.android.launcher3.BubbleTextView;
41 import com.android.launcher3.CellLayout;
42 import com.android.launcher3.DeleteDropTarget;
43 import com.android.launcher3.DeviceProfile;
44 import com.android.launcher3.DragSource;
45 import com.android.launcher3.DropTarget;
46 import com.android.launcher3.ExtendedEditText;
47 import com.android.launcher3.ItemInfo;
48 import com.android.launcher3.Launcher;
49 import com.android.launcher3.LauncherTransitionable;
50 import com.android.launcher3.R;
51 import com.android.launcher3.Utilities;
52 import com.android.launcher3.Workspace;
53 import com.android.launcher3.config.FeatureFlags;
54 import com.android.launcher3.folder.Folder;
55 import com.android.launcher3.keyboard.FocusedItemDecorator;
56 import com.android.launcher3.util.ComponentKey;
58 import java.nio.charset.Charset;
59 import java.nio.charset.CharsetEncoder;
60 import java.util.ArrayList;
61 import java.util.List;
65 * A merge algorithm that merges every section indiscriminately.
67 final class FullMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm {
70 public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
71 AlphabeticalAppsList.SectionInfo withSection,
72 int sectionAppCount, int numAppsPerRow, int mergeCount) {
73 // Don't merge the predicted apps
74 if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
77 // Otherwise, merge every other section
83 * The logic we use to merge multiple sections. We only merge sections when their final row
84 * contains less than a certain number of icons, and stop at a specified max number of merges.
85 * In addition, we will try and not merge sections that identify apps from different scripts.
87 final class SimpleSectionMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm {
89 private int mMinAppsPerRow;
90 private int mMinRowsInMergedSection;
91 private int mMaxAllowableMerges;
92 private CharsetEncoder mAsciiEncoder;
94 public SimpleSectionMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) {
95 mMinAppsPerRow = minAppsPerRow;
96 mMinRowsInMergedSection = minRowsInMergedSection;
97 mMaxAllowableMerges = maxNumMerges;
98 mAsciiEncoder = Charset.forName("US-ASCII").newEncoder();
102 public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
103 AlphabeticalAppsList.SectionInfo withSection,
104 int sectionAppCount, int numAppsPerRow, int mergeCount) {
105 // Don't merge the predicted apps
106 if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
110 // Continue merging if the number of hanging apps on the final row is less than some
111 // fixed number (ragged), the merged rows has yet to exceed some minimum row count,
112 // and while the number of merged sections is less than some fixed number of merges
113 int rows = sectionAppCount / numAppsPerRow;
114 int cols = sectionAppCount % numAppsPerRow;
116 // Ensure that we do not merge across scripts, currently we only allow for english and
117 // native scripts so we can test if both can just be ascii encoded
118 boolean isCrossScript = false;
119 if (section.firstAppItem != null && withSection.firstAppItem != null) {
120 isCrossScript = mAsciiEncoder.canEncode(section.firstAppItem.sectionName) !=
121 mAsciiEncoder.canEncode(withSection.firstAppItem.sectionName);
123 return (0 < cols && cols < mMinAppsPerRow) &&
124 rows < mMinRowsInMergedSection &&
125 mergeCount < mMaxAllowableMerges &&
131 * The all apps view container.
133 public class AllAppsContainerView extends BaseContainerView implements DragSource,
134 LauncherTransitionable, View.OnLongClickListener, AllAppsSearchBarController.Callbacks {
136 private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
137 private static final int MAX_NUM_MERGES_PHONE = 2;
139 private final Launcher mLauncher;
140 private final AlphabeticalAppsList mApps;
141 private final AllAppsGridAdapter mAdapter;
142 private final RecyclerView.LayoutManager mLayoutManager;
143 private final RecyclerView.ItemDecoration mItemDecoration;
145 // The computed bounds of the container
146 private final Rect mContentBounds = new Rect();
148 private AllAppsRecyclerView mAppsRecyclerView;
149 private AllAppsSearchBarController mSearchBarController;
151 private View mSearchContainer;
152 private ExtendedEditText mSearchInput;
153 private ImageView mSearchIcon;
154 private HeaderElevationController mElevationController;
156 private SpannableStringBuilder mSearchQueryBuilder = null;
158 private int mSectionNamesMargin;
159 private int mNumAppsPerRow;
160 private int mNumPredictedAppsPerRow;
161 private int mRecyclerViewTopBottomPadding;
162 // This coordinate is relative to this container view
163 private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1);
165 public AllAppsContainerView(Context context) {
169 public AllAppsContainerView(Context context, AttributeSet attrs) {
170 this(context, attrs, 0);
173 public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
174 super(context, attrs, defStyleAttr);
175 Resources res = context.getResources();
177 mLauncher = Launcher.getLauncher(context);
178 mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
179 mApps = new AlphabeticalAppsList(context);
180 mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);
181 mApps.setAdapter(mAdapter);
182 mLayoutManager = mAdapter.getLayoutManager();
183 mItemDecoration = mAdapter.getItemDecoration();
184 DeviceProfile grid = mLauncher.getDeviceProfile();
185 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && !grid.isVerticalBarLayout()) {
186 mRecyclerViewTopBottomPadding = 0;
187 setPadding(0, 0, 0, 0);
189 mRecyclerViewTopBottomPadding =
190 res.getDimensionPixelSize(R.dimen.all_apps_list_top_bottom_padding);
192 mSearchQueryBuilder = new SpannableStringBuilder();
193 Selection.setSelection(mSearchQueryBuilder, 0);
197 * Sets the current set of predicted apps.
199 public void setPredictedApps(List<ComponentKey> apps) {
200 mApps.setPredictedApps(apps);
204 * Sets the current set of apps.
206 public void setApps(List<AppInfo> apps) {
211 * Adds new apps to the list.
213 public void addApps(List<AppInfo> apps) {
218 * Updates existing apps in the list
220 public void updateApps(List<AppInfo> apps) {
221 mApps.updateApps(apps);
225 * Removes some apps from the list.
227 public void removeApps(List<AppInfo> apps) {
228 mApps.removeApps(apps);
231 public void setSearchBarVisible(boolean visible) {
233 mSearchBarController.setVisibility(View.VISIBLE);
235 mSearchBarController.setVisibility(View.INVISIBLE);
240 * Sets the search bar that shows above the a-z list.
242 public void setSearchBarController(AllAppsSearchBarController searchController) {
243 if (mSearchBarController != null) {
244 throw new RuntimeException("Expected search bar controller to only be set once");
246 mSearchBarController = searchController;
247 mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
248 mAdapter.setSearchController(mSearchBarController);
252 * Scrolls this list view to the top.
254 public void scrollToTop() {
255 mAppsRecyclerView.scrollToTop();
259 * Returns whether the view itself will handle the touch event or not.
261 public boolean shouldContainerScroll(float x, float y) {
262 int[] point = new int[2];
265 Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, point);
267 // if the MotionEvent is inside the thumb, container should not be pulled down.
268 if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) {
271 // If scroller is at the very top, then it's okay for the container to be pulled down.
272 if (Float.compare(0f, mAppsRecyclerView.getScrollBar().getThumbOffset().y) == 0) {
279 * Focuses the search field and begins an app search.
281 public void startAppsSearch() {
282 if (mSearchBarController != null) {
283 mSearchBarController.focusSearchField();
288 * Resets the state of AllApps.
290 public void reset() {
291 // 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);
313 mSearchIcon = (ImageView) findViewById(R.id.search_icon);
315 final LinearLayout.LayoutParams searchParams =
316 (LinearLayout.LayoutParams) mSearchInput.getLayoutParams();
317 mSearchInput.setOnFocusChangeListener(new OnFocusChangeListener() {
319 public void onFocusChange(View view, boolean focused) {
321 searchParams.width = LayoutParams.MATCH_PARENT;
322 mSearchInput.setLayoutParams(searchParams);
323 mSearchInput.setGravity(Gravity.FILL_HORIZONTAL | Gravity.CENTER_VERTICAL);
324 mSearchIcon.setVisibility(View.GONE);
326 searchParams.width = LayoutParams.WRAP_CONTENT;
327 mSearchInput.setLayoutParams(searchParams);
328 mSearchInput.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
329 mSearchIcon.setVisibility(View.VISIBLE);
334 final OnClickListener searchFocusListener = new OnClickListener() {
336 public void onClick(View view) {
337 if (!mSearchInput.isFocused()) {
338 mSearchInput.requestFocus();
339 final InputMethodManager imm =
340 (InputMethodManager)getContext().getSystemService(
341 Context.INPUT_METHOD_SERVICE);
342 imm.showSoftInput(mSearchInput, 0);
346 mSearchInput.setOnClickListener(searchFocusListener);
347 mSearchContainer.setOnClickListener(searchFocusListener);
349 mElevationController = Utilities.ATLEAST_LOLLIPOP
350 ? new HeaderElevationController.ControllerVL(mSearchContainer)
351 : new HeaderElevationController.ControllerV16(mSearchContainer);
353 // Load the all apps recycler view
354 mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
355 mAppsRecyclerView.setApps(mApps);
356 mAppsRecyclerView.setLayoutManager(mLayoutManager);
357 mAppsRecyclerView.setAdapter(mAdapter);
358 mAppsRecyclerView.setHasFixedSize(true);
359 mAppsRecyclerView.addOnScrollListener(mElevationController);
360 mAppsRecyclerView.setElevationController(mElevationController);
362 if (mItemDecoration != null) {
363 mAppsRecyclerView.addItemDecoration(mItemDecoration);
366 FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
367 mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
368 mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
370 // Precalculate the prediction icon and normal icon sizes
371 LayoutInflater layoutInflater = LayoutInflater.from(getContext());
372 final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
373 getResources().getDisplayMetrics().widthPixels, MeasureSpec.AT_MOST);
374 final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(
375 getResources().getDisplayMetrics().heightPixels, MeasureSpec.AT_MOST);
377 BubbleTextView icon = (BubbleTextView) layoutInflater.inflate(
378 R.layout.all_apps_icon, this, false);
379 icon.applyDummyInfo();
380 icon.measure(widthMeasureSpec, heightMeasureSpec);
381 BubbleTextView predIcon = (BubbleTextView) layoutInflater.inflate(
382 R.layout.all_apps_prediction_bar_icon, this, false);
383 predIcon.applyDummyInfo();
384 predIcon.measure(widthMeasureSpec, heightMeasureSpec);
385 mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(),
386 icon.getMeasuredHeight());
388 // TODO(hyunyoungs): clean up setting the content and the reveal view.
389 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
390 getContentView().setBackground(null);
391 getRevealView().setVisibility(View.VISIBLE);
392 getRevealView().setAlpha(AllAppsTransitionController.ALL_APPS_FINAL_ALPHA);
397 public void onBoundsChanged(Rect newBounds) { }
400 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
401 updatePaddingsAndMargins();
402 mContentBounds.set(mHorizontalPadding, 0,
403 MeasureSpec.getSize(widthMeasureSpec) - mHorizontalPadding,
404 MeasureSpec.getSize(heightMeasureSpec));
406 DeviceProfile grid = mLauncher.getDeviceProfile();
407 int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() :
408 MeasureSpec.getSize(widthMeasureSpec))
409 - 2 * mAppsRecyclerView.getMaxScrollbarWidth();
410 grid.updateAppsViewNumCols(getResources(), availableWidth);
411 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
412 if (mNumAppsPerRow != grid.inv.numColumns ||
413 mNumPredictedAppsPerRow != grid.inv.numColumns) {
414 mNumAppsPerRow = grid.inv.numColumns;
415 mNumPredictedAppsPerRow = grid.inv.numColumns;
417 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
418 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
419 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, new FullMergeAlgorithm());
420 if (mNumAppsPerRow > 0) {
421 int iconSize = availableWidth / mNumAppsPerRow;
422 int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
423 final int thumbMaxWidth =
424 getResources().getDimensionPixelSize(
425 R.dimen.container_fastscroll_thumb_max_width);
426 mSearchContainer.setPaddingRelative(
427 iconSpacing + thumbMaxWidth, 0, iconSpacing + thumbMaxWidth, 0);
430 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
434 // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
436 // Update the number of items in the grid before we measure the view
437 // TODO: mSectionNamesMargin is currently 0, but also account for it,
438 // if it's enabled in the future.
439 grid.updateAppsViewNumCols(getResources(), availableWidth);
440 if (mNumAppsPerRow != grid.allAppsNumCols ||
441 mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
442 mNumAppsPerRow = grid.allAppsNumCols;
443 mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
445 // If there is a start margin to draw section names, determine how we are going to merge
447 boolean mergeSectionsFully = mSectionNamesMargin == 0 || !grid.isPhone;
448 AlphabeticalAppsList.MergeAlgorithm mergeAlgorithm = mergeSectionsFully ?
449 new FullMergeAlgorithm() :
450 new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f),
451 MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
453 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
454 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
455 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
457 // TODO: should we not do all this complicated computation but just match the
458 // numAppsPerRow with the workspace?
459 if (mNumAppsPerRow > 0) {
460 int iconSize = availableWidth / mNumAppsPerRow;
461 int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
462 mSearchInput.setPaddingRelative(iconSpacing, 0, iconSpacing, 0);
466 // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
467 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
471 * Update the background and padding of the Apps view and children. Instead of insetting the
472 * container view, we inset the background and padding of the recycler view to allow for the
473 * recycler view to handle touch events (for fast scrolling) all the way to the edge.
475 private void updatePaddingsAndMargins() {
476 Rect bgPadding = new Rect();
477 getRevealView().getBackground().getPadding(bgPadding);
479 mAppsRecyclerView.updateBackgroundPadding(bgPadding);
480 mAdapter.updateBackgroundPadding(bgPadding);
481 mElevationController.updateBackgroundPadding(bgPadding);
483 // Pad the recycler view by the background padding plus the start margin (for the section
485 int maxScrollBarWidth = mAppsRecyclerView.getMaxScrollbarWidth();
486 int startInset = Math.max(mSectionNamesMargin, maxScrollBarWidth);
487 int topBottomPadding = mRecyclerViewTopBottomPadding;
488 if (Utilities.isRtl(getResources())) {
489 mAppsRecyclerView.setPadding(bgPadding.left + maxScrollBarWidth,
490 topBottomPadding, bgPadding.right + startInset, topBottomPadding);
492 mAppsRecyclerView.setPadding(bgPadding.left + startInset, topBottomPadding,
493 bgPadding.right + maxScrollBarWidth, topBottomPadding);
496 MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
497 lp.leftMargin = bgPadding.left;
498 lp.rightMargin = bgPadding.right;
500 // Clip the view to the left and right edge of the background to
501 // to prevent shadows from rendering beyond the edges
502 final Rect newClipBounds = new Rect(
505 getWidth() - bgPadding.right,
508 setClipBounds(newClipBounds);
510 // Allow the overscroll effect to reach the edges of the view
511 mAppsRecyclerView.setClipToPadding(false);
513 DeviceProfile grid = mLauncher.getDeviceProfile();
514 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
515 if (!grid.isVerticalBarLayout()) {
516 MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams();
518 Rect insets = mLauncher.getDragLayer().getInsets();
519 getContentView().setPadding(0, 0, 0, insets.bottom);
520 int height = insets.top + grid.hotseatCellHeightPx;
522 mlp.topMargin = height;
523 mAppsRecyclerView.setLayoutParams(mlp);
525 LinearLayout.LayoutParams llp =
526 (LinearLayout.LayoutParams) mSearchInput.getLayoutParams();
527 llp.topMargin = insets.top;
528 mSearchInput.setLayoutParams(llp);
529 mSearchIcon.setLayoutParams(llp);
534 mSearchContainer.setLayoutParams(lp);
538 public boolean dispatchKeyEvent(KeyEvent event) {
539 // Determine if the key event was actual text, if so, focus the search bar and then dispatch
540 // the key normally so that it can process this key event
541 if (!mSearchBarController.isSearchFieldFocused() &&
542 event.getAction() == KeyEvent.ACTION_DOWN) {
543 final int unicodeChar = event.getUnicodeChar();
544 final boolean isKeyNotWhitespace = unicodeChar > 0 &&
545 !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
546 if (isKeyNotWhitespace) {
547 boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
548 event.getKeyCode(), event);
549 if (gotKey && mSearchQueryBuilder.length() > 0) {
550 mSearchBarController.focusSearchField();
555 return super.dispatchKeyEvent(event);
559 public boolean onInterceptTouchEvent(MotionEvent ev) {
560 return handleTouchEvent(ev);
563 @SuppressLint("ClickableViewAccessibility")
565 public boolean onTouchEvent(MotionEvent ev) {
566 return handleTouchEvent(ev);
570 public boolean onLongClick(View v) {
571 // Return early if this is not initiated from a touch
572 if (!v.isInTouchMode()) return false;
573 // When we have exited all apps or are in transition, disregard long clicks
575 if (!mLauncher.isAppsViewVisible() ||
576 mLauncher.getWorkspace().isSwitchingState()) return false;
577 // Return if global dragging is not enabled
578 if (!mLauncher.isDraggingEnabled()) return false;
581 mLauncher.getWorkspace().beginDragShared(v, this, false);
582 // Enter spring loaded mode
583 mLauncher.enterSpringLoadedDragMode();
589 public boolean supportsFlingToDelete() {
594 public boolean supportsAppInfoDropTarget() {
599 public boolean supportsDeleteDropTarget() {
604 public float getIntrinsicIconScaleFactor() {
605 DeviceProfile grid = mLauncher.getDeviceProfile();
606 return (float) grid.allAppsIconSizePx / grid.iconSizePx;
610 public void onFlingToDeleteCompleted() {
611 // We just dismiss the drag when we fling, so cleanup here
612 mLauncher.exitSpringLoadedDragModeDelayed(true,
613 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
614 mLauncher.unlockScreenOrientation(false);
618 public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
620 if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
621 !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
622 // Exit spring loaded mode if we have not successfully dropped or have not handled the
624 mLauncher.exitSpringLoadedDragModeDelayed(true,
625 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
627 mLauncher.unlockScreenOrientation(false);
629 // Display an error message if the drag failed due to there not being enough space on the
630 // target layout we were dropping on.
632 boolean showOutOfSpaceMessage = false;
633 if (target instanceof Workspace) {
634 int currentScreen = mLauncher.getCurrentWorkspaceScreen();
635 Workspace workspace = (Workspace) target;
636 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
637 ItemInfo itemInfo = d.dragInfo;
638 if (layout != null) {
639 showOutOfSpaceMessage =
640 !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
643 if (showOutOfSpaceMessage) {
644 mLauncher.showOutOfSpaceMessage(false);
647 d.deferDragViewCleanupPostAnimation = false;
652 public void onLauncherTransitionPrepare(Launcher l, boolean animated,
653 boolean multiplePagesVisible) {
658 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
663 public void onLauncherTransitionStep(Launcher l, float t) {
668 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
675 * Handles the touch events to dismiss all apps when clicking outside the bounds of the
678 private boolean handleTouchEvent(MotionEvent ev) {
679 DeviceProfile grid = mLauncher.getDeviceProfile();
680 int x = (int) ev.getX();
681 int y = (int) ev.getY();
683 switch (ev.getAction()) {
684 case MotionEvent.ACTION_DOWN:
685 if (!mContentBounds.isEmpty()) {
686 // Outset the fixed bounds and check if the touch is outside all apps
687 Rect tmpRect = new Rect(mContentBounds);
688 tmpRect.inset(-grid.allAppsIconSizePx / 2, 0);
689 if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) {
690 mBoundsCheckLastTouchDownPos.set(x, y);
694 // Check if the touch is outside all apps
695 if (ev.getX() < getPaddingLeft() ||
696 ev.getX() > (getWidth() - getPaddingRight())) {
697 mBoundsCheckLastTouchDownPos.set(x, y);
702 case MotionEvent.ACTION_UP:
703 if (mBoundsCheckLastTouchDownPos.x > -1) {
704 ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
705 float dx = ev.getX() - mBoundsCheckLastTouchDownPos.x;
706 float dy = ev.getY() - mBoundsCheckLastTouchDownPos.y;
707 float distance = (float) Math.hypot(dx, dy);
708 if (distance < viewConfig.getScaledTouchSlop()) {
709 // The background was clicked, so just go home
710 Launcher launcher = (Launcher) getContext();
711 launcher.showWorkspace(true);
716 case MotionEvent.ACTION_CANCEL:
717 mBoundsCheckLastTouchDownPos.set(-1, -1);
724 public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
726 if (mApps.setOrderedFilter(apps)) {
727 mAppsRecyclerView.onSearchResultsChanged();
729 mAdapter.setLastSearchQuery(query);
734 public void clearSearchResult() {
735 if (mApps.setOrderedFilter(null)) {
736 mAppsRecyclerView.onSearchResultsChanged();
739 // Clear the search query
740 mSearchQueryBuilder.clear();
741 mSearchQueryBuilder.clearSpans();
742 Selection.setSelection(mSearchQueryBuilder, 0);