Fixing issue with touch scrolling the prediction bar.

Change-Id: Ie15ca96e5ea33a54508285aa76fb6aea81b2376d
This commit is contained in:
Winson Chung
2015-05-13 15:44:26 -07:00
parent 358af81ca4
commit 6b27614c54
3 changed files with 88 additions and 16 deletions
@@ -34,7 +34,6 @@ import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.util.Thunk;
@@ -82,8 +81,12 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
private Point mLastTouchDownPos = new Point(-1, -1);
private Point mLastTouchPos = new Point();
// This coordinate is relative to this container view
private Point mBoundsCheckLastTouchDownPos = new Point(-1, -1);
// This coordinate is relative to its parent
private Point mIconLastTouchPos = new Point();
// This coordinate is used to proxy click and long-click events
private Point mPredictionIconTouchDownPos = new Point();
private int mContentMarginStart;
// Normal container insets
private int mContainerInset;
@@ -91,6 +94,9 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
// RecyclerView scroll position
@Thunk int mRecyclerViewScrollY;
private CheckLongPressHelper mPredictionIconCheckForLongPress;
private View mPredictionIconUnderTouch;
public AppsContainerView(Context context) {
this(context, null);
}
@@ -283,9 +289,6 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
// Otherwise, inflate a new icon
icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.apps_prediction_bar_icon_view, mPredictionBarView, false);
icon.setOnTouchListener(this);
icon.setOnClickListener(mLauncher);
icon.setOnLongClickListener(this);
icon.setFocusable(true);
mPredictionBarView.addView(icon);
}
@@ -401,7 +404,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mLastTouchPos.set((int) ev.getX(), (int) ev.getY());
mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
break;
}
return false;
@@ -427,7 +430,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
if (!mLauncher.isDraggingEnabled()) return false;
// Start the drag
mLauncher.getWorkspace().beginDragShared(v, mLastTouchPos, this, false);
mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false);
// Enter spring loaded mode
mLauncher.enterSpringLoadedDragMode();
@@ -626,6 +629,16 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
mPredictionBarView.setTranslationY(-mRecyclerViewScrollY + mAppsRecyclerView.getPaddingTop());
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
// If we were waiting for long-click, cancel the request once a child has started handling
// the scrolling
if (mPredictionIconCheckForLongPress != null) {
mPredictionIconCheckForLongPress.cancelLongPress();
}
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
/**
* Handles the touch events to dismiss all apps when clicking outside the bounds of the
* recycler view.
@@ -633,31 +646,44 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
private boolean handleTouchEvent(MotionEvent ev) {
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// We workaround the fact that the recycler view needs the touches for the scroll
// and we want to intercept it for clicks in the prediction bar by handling clicks
// and long clicks in the prediction bar ourselves.
mPredictionIconTouchDownPos.set(x, y);
mPredictionIconUnderTouch = findPredictedAppAtCoordinate(x, y);
if (mPredictionIconUnderTouch != null) {
mPredictionIconCheckForLongPress =
new CheckLongPressHelper(mPredictionIconUnderTouch, this);
mPredictionIconCheckForLongPress.postCheckForLongPress();
}
if (!mFixedBounds.isEmpty()) {
// Outset the fixed bounds and check if the touch is outside all apps
Rect tmpRect = new Rect(mFixedBounds);
tmpRect.inset(-grid.allAppsIconSizePx / 2, 0);
if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) {
mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY());
mBoundsCheckLastTouchDownPos.set(x, y);
return true;
}
} else {
// Check if the touch is outside all apps
if (ev.getX() < getPaddingLeft() ||
ev.getX() > (getWidth() - getPaddingRight())) {
mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY());
mBoundsCheckLastTouchDownPos.set(x, y);
return true;
}
}
break;
case MotionEvent.ACTION_UP:
if (mLastTouchDownPos.x > -1) {
if (mBoundsCheckLastTouchDownPos.x > -1) {
ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
float dx = ev.getX() - mLastTouchDownPos.x;
float dy = ev.getY() - mLastTouchDownPos.y;
float dx = ev.getX() - mBoundsCheckLastTouchDownPos.x;
float dy = ev.getY() - mBoundsCheckLastTouchDownPos.y;
float distance = (float) Math.hypot(dx, dy);
if (distance < viewConfig.getScaledTouchSlop()) {
// The background was clicked, so just go home
@@ -666,14 +692,48 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
return true;
}
}
// Trigger the click on the prediction bar icon if that's where we touched
if (mPredictionIconUnderTouch != null &&
!mPredictionIconCheckForLongPress.hasPerformedLongPress()) {
mLauncher.onClick(mPredictionIconUnderTouch);
}
// Fall through
case MotionEvent.ACTION_CANCEL:
mLastTouchDownPos.set(-1, -1);
mBoundsCheckLastTouchDownPos.set(-1, -1);
mPredictionIconTouchDownPos.set(-1, -1);
// On touch up/cancel, cancel the long press on the prediction bar icon if it has
// not yet been performed
if (mPredictionIconCheckForLongPress != null) {
mPredictionIconCheckForLongPress.cancelLongPress();
mPredictionIconCheckForLongPress = null;
}
mPredictionIconUnderTouch = null;
break;
}
return false;
}
/**
* Returns the predicted app in the prediction bar given a set of local coordinates.
*/
private View findPredictedAppAtCoordinate(int x, int y) {
int[] coord = {x, y};
Rect hitRect = new Rect();
Utilities.mapCoordInSelfToDescendent(mPredictionBarView, this, coord);
for (int i = 0; i < mPredictionBarView.getChildCount(); i++) {
View child = mPredictionBarView.getChildAt(i);
child.getHitRect(hitRect);
if (hitRect.contains(coord[0], coord[1])) {
return child;
}
}
return null;
}
/**
* Shows the search field.
*/
@@ -22,6 +22,7 @@ import com.android.launcher3.util.Thunk;
public class CheckLongPressHelper {
@Thunk View mView;
@Thunk View.OnLongClickListener mListener;
@Thunk boolean mHasPerformedLongPress;
private CheckForLongPress mPendingCheckForLongPress;
@@ -29,7 +30,13 @@ public class CheckLongPressHelper {
public void run() {
if ((mView.getParent() != null) && mView.hasWindowFocus()
&& !mHasPerformedLongPress) {
if (mView.performLongClick()) {
boolean handled;
if (mListener != null) {
handled = mListener.onLongClick(mView);
} else {
handled = mView.performLongClick();
}
if (handled) {
mView.setPressed(false);
mHasPerformedLongPress = true;
}
@@ -41,6 +48,11 @@ public class CheckLongPressHelper {
mView = v;
}
public CheckLongPressHelper(View v, View.OnLongClickListener listener) {
mView = v;
mListener = listener;
}
public void postCheckForLongPress() {
mHasPerformedLongPress = false;
+1 -1
View File
@@ -443,7 +443,7 @@ public class DeviceProfile {
int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx;
int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
(allAppsCellWidthPx + 2 * allAppsCellPaddingPx);
int numPredictiveAppCols = isPhone() ? numColumns : numAppsCols;
int numPredictiveAppCols = Math.max(numColumns, numAppsCols);
if ((numAppsCols != appsViewNumCols) ||
(numPredictiveAppCols != appsViewNumPredictiveCols)) {
appsViewNumCols = numAppsCols;