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.ViewGroup;
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.VIEW_TYPE_ICON) {
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.VIEW_TYPE_ICON) {
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;
155 private int mSearchContainerOffsetTop;
157 private SpannableStringBuilder mSearchQueryBuilder = null;
159 private int mSectionNamesMargin;
160 private int mNumAppsPerRow;
161 private int mNumPredictedAppsPerRow;
162 private int mRecyclerViewBottomPadding;
163 // This coordinate is relative to this container view
164 private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1);
166 public AllAppsContainerView(Context context) {
170 public AllAppsContainerView(Context context, AttributeSet attrs) {
171 this(context, attrs, 0);
174 public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
175 super(context, attrs, defStyleAttr);
176 Resources res = context.getResources();
178 mLauncher = Launcher.getLauncher(context);
179 mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
180 mApps = new AlphabeticalAppsList(context);
181 mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);
182 mApps.setAdapter(mAdapter);
183 mLayoutManager = mAdapter.getLayoutManager();
184 mItemDecoration = mAdapter.getItemDecoration();
185 DeviceProfile grid = mLauncher.getDeviceProfile();
186 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && !grid.isVerticalBarLayout()) {
187 mRecyclerViewBottomPadding = 0;
188 setPadding(0, 0, 0, 0);
190 mRecyclerViewBottomPadding =
191 res.getDimensionPixelSize(R.dimen.all_apps_list_bottom_padding);
193 mSearchQueryBuilder = new SpannableStringBuilder();
194 Selection.setSelection(mSearchQueryBuilder, 0);
198 * Sets the current set of predicted apps.
200 public void setPredictedApps(List<ComponentKey> apps) {
201 mApps.setPredictedApps(apps);
205 * Sets the current set of apps.
207 public void setApps(List<AppInfo> apps) {
212 * Adds new apps to the list.
214 public void addApps(List<AppInfo> apps) {
219 * Updates existing apps in the list
221 public void updateApps(List<AppInfo> apps) {
222 mApps.updateApps(apps);
226 * Removes some apps from the list.
228 public void removeApps(List<AppInfo> apps) {
229 mApps.removeApps(apps);
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, then it's okay for the container to be pulled down.
273 if (Float.compare(0f, mAppsRecyclerView.getScrollBar().getThumbOffset().y) == 0) {
280 * Focuses the search field and begins an app search.
282 public void startAppsSearch() {
283 if (mSearchBarController != null) {
284 mSearchBarController.focusSearchField();
289 * Resets the state of AllApps.
291 public void reset() {
292 // Reset the search bar and base recycler view after transitioning home
293 mSearchBarController.reset();
294 mAppsRecyclerView.reset();
298 protected void onFinishInflate() {
299 super.onFinishInflate();
301 // This is a focus listener that proxies focus from a view into the list view. This is to
302 // work around the search box from getting first focus and showing the cursor.
303 getContentView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
305 public void onFocusChange(View v, boolean hasFocus) {
307 mAppsRecyclerView.requestFocus();
312 mSearchContainer = findViewById(R.id.search_container);
313 mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
314 mSearchIcon = (ImageView) findViewById(R.id.search_icon);
315 mSearchContainerOffsetTop = getResources().getDimensionPixelSize(
316 R.dimen.all_apps_search_bar_margin_top);
318 final LinearLayout.LayoutParams searchParams =
319 (LinearLayout.LayoutParams) mSearchInput.getLayoutParams();
320 mSearchInput.setOnFocusChangeListener(new OnFocusChangeListener() {
322 public void onFocusChange(View view, boolean focused) {
324 searchParams.width = LayoutParams.MATCH_PARENT;
325 mSearchInput.setLayoutParams(searchParams);
326 mSearchInput.setGravity(Gravity.FILL_HORIZONTAL | Gravity.CENTER_VERTICAL);
327 mSearchIcon.setVisibility(View.GONE);
329 searchParams.width = LayoutParams.WRAP_CONTENT;
330 mSearchInput.setLayoutParams(searchParams);
331 mSearchInput.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
332 mSearchIcon.setVisibility(View.VISIBLE);
337 final OnClickListener searchFocusListener = new OnClickListener() {
339 public void onClick(View view) {
340 if (!mSearchBarController.isSearchFieldFocused()) {
341 mSearchBarController.focusSearchField();
345 mSearchInput.setOnClickListener(searchFocusListener);
346 mSearchContainer.setOnClickListener(searchFocusListener);
348 mElevationController = Utilities.ATLEAST_LOLLIPOP
349 ? new HeaderElevationController.ControllerVL(mSearchContainer)
350 : new HeaderElevationController.ControllerV16(mSearchContainer);
352 // Load the all apps recycler view
353 mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
354 mAppsRecyclerView.setApps(mApps);
355 mAppsRecyclerView.setLayoutManager(mLayoutManager);
356 mAppsRecyclerView.setAdapter(mAdapter);
357 mAppsRecyclerView.setHasFixedSize(true);
358 mAppsRecyclerView.addOnScrollListener(mElevationController);
359 mAppsRecyclerView.setElevationController(mElevationController);
361 if (mItemDecoration != null) {
362 mAppsRecyclerView.addItemDecoration(mItemDecoration);
365 FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
366 mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
367 mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
369 // Precalculate the prediction icon and normal icon sizes
370 LayoutInflater layoutInflater = LayoutInflater.from(getContext());
371 final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
372 getResources().getDisplayMetrics().widthPixels, MeasureSpec.AT_MOST);
373 final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(
374 getResources().getDisplayMetrics().heightPixels, MeasureSpec.AT_MOST);
376 BubbleTextView icon = (BubbleTextView) layoutInflater.inflate(
377 R.layout.all_apps_icon, this, false);
378 icon.applyDummyInfo();
379 icon.measure(widthMeasureSpec, heightMeasureSpec);
380 BubbleTextView predIcon = (BubbleTextView) layoutInflater.inflate(
381 R.layout.all_apps_prediction_bar_icon, this, false);
382 predIcon.applyDummyInfo();
383 predIcon.measure(widthMeasureSpec, heightMeasureSpec);
384 mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(),
385 icon.getMeasuredHeight());
387 // TODO(hyunyoungs): clean up setting the content and the reveal view.
388 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
389 getContentView().setBackground(null);
390 getRevealView().setVisibility(View.VISIBLE);
391 getRevealView().setAlpha(AllAppsTransitionController.ALL_APPS_FINAL_ALPHA);
396 public void onBoundsChanged(Rect newBounds) { }
399 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
400 int widthPx = MeasureSpec.getSize(widthMeasureSpec);
401 int heightPx = MeasureSpec.getSize(heightMeasureSpec);
402 updatePaddingsAndMargins(widthPx, heightPx);
403 mContentBounds.set(mHorizontalPadding, 0, widthPx - mHorizontalPadding, heightPx);
405 DeviceProfile grid = mLauncher.getDeviceProfile();
406 int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() : widthPx)
407 - 2 * mAppsRecyclerView.getMaxScrollbarWidth();
408 grid.updateAppsViewNumCols(getResources(), availableWidth);
409 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
410 if (mNumAppsPerRow != grid.inv.numColumns ||
411 mNumPredictedAppsPerRow != grid.inv.numColumns) {
412 mNumAppsPerRow = grid.inv.numColumns;
413 mNumPredictedAppsPerRow = grid.inv.numColumns;
415 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
416 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
417 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, new FullMergeAlgorithm());
418 if (mNumAppsPerRow > 0) {
419 int rvPadding = mAppsRecyclerView.getPaddingStart(); // Assumes symmetry
420 final int thumbMaxWidth =
421 getResources().getDimensionPixelSize(
422 R.dimen.container_fastscroll_thumb_max_width);
423 mSearchContainer.setPaddingRelative(rvPadding + thumbMaxWidth, 0, rvPadding +
427 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
431 // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
433 // Update the number of items in the grid before we measure the view
434 // TODO: mSectionNamesMargin is currently 0, but also account for it,
435 // if it's enabled in the future.
436 grid.updateAppsViewNumCols(getResources(), availableWidth);
437 if (mNumAppsPerRow != grid.allAppsNumCols ||
438 mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
439 mNumAppsPerRow = grid.allAppsNumCols;
440 mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
442 // If there is a start margin to draw section names, determine how we are going to merge
444 boolean mergeSectionsFully = mSectionNamesMargin == 0 || !grid.isPhone;
445 AlphabeticalAppsList.MergeAlgorithm mergeAlgorithm = mergeSectionsFully ?
446 new FullMergeAlgorithm() :
447 new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f),
448 MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
450 mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
451 mAdapter.setNumAppsPerRow(mNumAppsPerRow);
452 mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
454 // TODO: should we not do all this complicated computation but just match the
455 // numAppsPerRow with the workspace?
456 if (mNumAppsPerRow > 0) {
457 int iconSize = availableWidth / mNumAppsPerRow;
458 int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
459 mSearchInput.setPaddingRelative(iconSpacing, 0, iconSpacing, 0);
463 // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
464 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
468 * Update the background and padding of the Apps view and children. Instead of insetting the
469 * container view, we inset the background and padding of the recycler view to allow for the
470 * recycler view to handle touch events (for fast scrolling) all the way to the edge.
472 private void updatePaddingsAndMargins(int widthPx, int heightPx) {
473 Rect bgPadding = new Rect();
474 getRevealView().getBackground().getPadding(bgPadding);
476 mAppsRecyclerView.updateBackgroundPadding(bgPadding);
477 mAdapter.updateBackgroundPadding(bgPadding);
478 mElevationController.updateBackgroundPadding(bgPadding);
480 // Pad the recycler view by the background padding plus the start margin (for the section
482 int maxScrollBarWidth = mAppsRecyclerView.getMaxScrollbarWidth();
483 int startInset = Math.max(mSectionNamesMargin, maxScrollBarWidth);
484 if (Utilities.isRtl(getResources())) {
485 mAppsRecyclerView.setPadding(bgPadding.left + maxScrollBarWidth, 0, bgPadding.right
486 + startInset, mRecyclerViewBottomPadding);
488 mAppsRecyclerView.setPadding(bgPadding.left + startInset, 0, bgPadding.right +
489 maxScrollBarWidth, mRecyclerViewBottomPadding);
492 MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
493 lp.leftMargin = bgPadding.left;
494 lp.rightMargin = bgPadding.right;
496 // Clip the view to the left and right edge of the background to
497 // to prevent shadows from rendering beyond the edges
498 final Rect newClipBounds = new Rect(
499 bgPadding.left, 0, widthPx - bgPadding.right, heightPx);
500 setClipBounds(newClipBounds);
502 // Allow the overscroll effect to reach the edges of the view
503 mAppsRecyclerView.setClipToPadding(false);
505 DeviceProfile grid = mLauncher.getDeviceProfile();
506 if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
507 if (!grid.isVerticalBarLayout()) {
508 MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams();
510 Rect insets = mLauncher.getDragLayer().getInsets();
511 getContentView().setPadding(0, 0, 0, insets.bottom);
512 int height = insets.top + grid.hotseatCellHeightPx;
514 mlp.topMargin = height;
515 mAppsRecyclerView.setLayoutParams(mlp);
517 LinearLayout.LayoutParams llp =
518 (LinearLayout.LayoutParams) mSearchInput.getLayoutParams();
519 llp.topMargin = insets.top + mSearchContainerOffsetTop;
520 mSearchInput.setLayoutParams(llp);
521 mSearchIcon.setLayoutParams(llp);
525 View navBarBg = findViewById(R.id.nav_bar_bg);
526 ViewGroup.LayoutParams params = navBarBg.getLayoutParams();
527 params.height = insets.bottom;
528 navBarBg.setLayoutParams(params);
529 navBarBg.setVisibility(View.VISIBLE);
532 mSearchContainer.setLayoutParams(lp);
536 public boolean dispatchKeyEvent(KeyEvent event) {
537 // Determine if the key event was actual text, if so, focus the search bar and then dispatch
538 // the key normally so that it can process this key event
539 if (!mSearchBarController.isSearchFieldFocused() &&
540 event.getAction() == KeyEvent.ACTION_DOWN) {
541 final int unicodeChar = event.getUnicodeChar();
542 final boolean isKeyNotWhitespace = unicodeChar > 0 &&
543 !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
544 if (isKeyNotWhitespace) {
545 boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
546 event.getKeyCode(), event);
547 if (gotKey && mSearchQueryBuilder.length() > 0) {
548 mSearchBarController.focusSearchField();
553 return super.dispatchKeyEvent(event);
557 public boolean onInterceptTouchEvent(MotionEvent ev) {
558 return handleTouchEvent(ev);
561 @SuppressLint("ClickableViewAccessibility")
563 public boolean onTouchEvent(MotionEvent ev) {
564 return handleTouchEvent(ev);
568 public boolean onLongClick(View v) {
569 // Return early if this is not initiated from a touch
570 if (!v.isInTouchMode()) return false;
571 // When we have exited all apps or are in transition, disregard long clicks
573 if (!mLauncher.isAppsViewVisible() ||
574 mLauncher.getWorkspace().isSwitchingState()) return false;
575 // Return if global dragging is not enabled
576 if (!mLauncher.isDraggingEnabled()) return false;
579 mLauncher.getWorkspace().beginDragShared(v, this, false);
580 // Enter spring loaded mode
581 mLauncher.enterSpringLoadedDragMode();
587 public boolean supportsFlingToDelete() {
592 public boolean supportsAppInfoDropTarget() {
597 public boolean supportsDeleteDropTarget() {
602 public float getIntrinsicIconScaleFactor() {
603 DeviceProfile grid = mLauncher.getDeviceProfile();
604 return (float) grid.allAppsIconSizePx / grid.iconSizePx;
608 public void onFlingToDeleteCompleted() {
609 // We just dismiss the drag when we fling, so cleanup here
610 mLauncher.exitSpringLoadedDragModeDelayed(true,
611 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
612 mLauncher.unlockScreenOrientation(false);
616 public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
618 if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
619 !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
620 // Exit spring loaded mode if we have not successfully dropped or have not handled the
622 mLauncher.exitSpringLoadedDragModeDelayed(true,
623 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
625 mLauncher.unlockScreenOrientation(false);
627 // Display an error message if the drag failed due to there not being enough space on the
628 // target layout we were dropping on.
630 boolean showOutOfSpaceMessage = false;
631 if (target instanceof Workspace) {
632 int currentScreen = mLauncher.getCurrentWorkspaceScreen();
633 Workspace workspace = (Workspace) target;
634 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
635 ItemInfo itemInfo = d.dragInfo;
636 if (layout != null) {
637 showOutOfSpaceMessage =
638 !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
641 if (showOutOfSpaceMessage) {
642 mLauncher.showOutOfSpaceMessage(false);
645 d.deferDragViewCleanupPostAnimation = false;
650 public void onLauncherTransitionPrepare(Launcher l, boolean animated,
651 boolean multiplePagesVisible) {
656 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
661 public void onLauncherTransitionStep(Launcher l, float t) {
666 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
673 * Handles the touch events to dismiss all apps when clicking outside the bounds of the
676 private boolean handleTouchEvent(MotionEvent ev) {
677 DeviceProfile grid = mLauncher.getDeviceProfile();
678 int x = (int) ev.getX();
679 int y = (int) ev.getY();
681 switch (ev.getAction()) {
682 case MotionEvent.ACTION_DOWN:
683 if (!mContentBounds.isEmpty()) {
684 // Outset the fixed bounds and check if the touch is outside all apps
685 Rect tmpRect = new Rect(mContentBounds);
686 tmpRect.inset(-grid.allAppsIconSizePx / 2, 0);
687 if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) {
688 mBoundsCheckLastTouchDownPos.set(x, y);
692 // Check if the touch is outside all apps
693 if (ev.getX() < getPaddingLeft() ||
694 ev.getX() > (getWidth() - getPaddingRight())) {
695 mBoundsCheckLastTouchDownPos.set(x, y);
700 case MotionEvent.ACTION_UP:
701 if (mBoundsCheckLastTouchDownPos.x > -1) {
702 ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
703 float dx = ev.getX() - mBoundsCheckLastTouchDownPos.x;
704 float dy = ev.getY() - mBoundsCheckLastTouchDownPos.y;
705 float distance = (float) Math.hypot(dx, dy);
706 if (distance < viewConfig.getScaledTouchSlop()) {
707 // The background was clicked, so just go home
708 Launcher launcher = (Launcher) getContext();
709 launcher.showWorkspace(true);
714 case MotionEvent.ACTION_CANCEL:
715 mBoundsCheckLastTouchDownPos.set(-1, -1);
722 public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
724 if (mApps.setOrderedFilter(apps)) {
725 mAppsRecyclerView.onSearchResultsChanged();
727 mAdapter.setLastSearchQuery(query);
732 public void clearSearchResult() {
733 if (mApps.setOrderedFilter(null)) {
734 mAppsRecyclerView.onSearchResultsChanged();
737 // Clear the search query
738 mSearchQueryBuilder.clear();
739 mSearchQueryBuilder.clearSpans();
740 Selection.setSelection(mSearchQueryBuilder, 0);